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.
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?
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.
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.
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")
Related
I noticed that when right clicking a text field in a wxPython widget, if the user selects the Insert Unicode control character option and inserts any of those charactersthe program will throw this error;
wx._core.wxAssertionError: C++ assertion "m_menuDepth > 0" failed at ..\..\src\msw\toplevel.cpp(1545) in wxTopLevelWindowMSW::DoSendMenuOpenCloseEvent(): No open menus?
The above exception was the direct cause of the following exception:
SystemError: <class 'wx._core.CommandEvent'> returned a result with an error set
Here's an example code which will throw the error;
import wx
class Program(wx.Frame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.panel = wx.Panel(self)
self.sizer = wx.GridBagSizer(0, 0)
self.panel.SetSizer(self.sizer)
self.text_ctrl = wx.TextCtrl(self.panel)
self.sizer.Add(self.text_ctrl, wx.GBPosition(0, 0))
# Needed, because there probably has to be some events which
# can be processed for the error to show before closing the program.
# Without this the error only appears after exiting.
self.text_ctrl.Bind(wx.EVT_TEXT, lambda e: print("text changed"))
self.sizer.Layout()
self.Show()
app = wx.App()
program = Program(parent=None)
app.MainLoop()
How can this error be prevented without redoing the context menu from scratch and without disabling it completely and without wrapping lots of stuff in try-except (really bad habit to catch wx._core.wxAssertionError, because they are generally fatal)?
Edit:
The bug is reproducable on:
Operating system: Win 10 Enterprise 2016 LTSB / Win 10 Pro (tested on both)
wxPython version & source: 4.0.2 / 4.0.3 from pip3 (tested on both)
Python version & source: Python 3.6.5 stock
This is a bug with wxPython and will be fixed in a future release.
The issue can be tracked here: https://github.com/wxWidgets/Phoenix/issues/992
For a project I am doing i need a console window to become the focus window when a hotkey is pressed. In this case it is F2. I have fixed the hotkey segment of the code now i need to have the console window become the focus.
I currently have:
import win32gui
import win32con, ctypes.wintypes, ctypes
def bringtofront():
#win32gui.ShowWindow(self, win32con.SW_MAXIMIZE) something like this?
print "now at the front!"
ctypes.windll.user32.RegisterHotKey(None, 1, 0, win32con.VK_F2)
while True:
try:
msg = ctypes.wintypes.MSG()
while ctypes.windll.user32.GetMessageA(ctypes.byref(msg), None, 0, 0) != 0:
if msg.message == win32con.WM_HOTKEY:
bringtofront()
ctypes.windll.user32.TranslateMessage(ctypes.byref(msg))
ctypes.windll.user32.DispatchMessageA(ctypes.byref(msg))
finally:
ctypes.windll.user32.UnregisterHotKey(None, 1)
Just to clarify the part of the code i am having trouble with is
win32gui.ShowWindow(self, win32con.SW_MAXIMIZE)
Based on https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548%28v=vs.85%29.aspx it would seem you need to use SW_SHOWMAXIMIZED, although I haven't tried it...
I am trying my first things with pywinauto.
Now I want to make use of print_control_identifiers() but I get errors, however I write my code - I cant get any information about GUI objects.
I already tried to generate the code via swapy - had a lot of generated code, but no success.
This is my code so far:
import getpass, fnmatch
from pywinauto import application
currentUser = getpass.getuser()
if fnmatch.fnmatch(currentUser, "axe"):
pwa_app = application.Application()
w_handle = application.findwindows.find_windows(title=u'Login - 0.9.347', class_name='WindowsForms10.Window.8.app.0.141b42a_r11_ad1')[0]
window = pwa_app.window_(handle=w_handle)
window.SetFocus()
ctrl = window['Log In']
ctrl.Click()
else:
print "You need admin rights for that action"
Can you tell me, where I need to use print_control_identifiers()?
Do you have any other GUI automation frameworks that are more up-to-date?
PrintControlIdentifiers() is useful for top level window. If window is top level window specification, then just call
window.PrintControlIdentifiers()
or
pwa_app.Window_(title=u'Login - 0.9.347', top_level_only=True).PrintControlIdentifiers()
A few examples:
Swapy is good to identify the properties. Also, the examples given with pywinauto are quite helpful.
Source: https://pywinauto.googlecode.com/hg/pywinauto/docs/getting_started.html
from pywinauto import application
app = application.Application.Start("Notepad.exe")
app.Notepad.print_control_identifiers()
app.Notepad.MenuSelect("Edit->Replace")
app.Replace.print_control_identifiers()
from pywinauto import application
from pywinauto import application
app = application.Application()
app.Start("Notepad.exe")
Wnd_Main = app.window_(title_re=".*Notepad")
Wnd_Main.MenuSelect("File->Save")
Wnd_Save = app.window_(title_re="Save As")
Wnd_Save.Edit1.SetEditText("Hello World.txt")
I wrote application in pygtk, that display a popup (like from Chrome) window, without resizing and moving option. All is great except one thing. I have to move this window to bottom of the screen, little above the taskbar.
Taskbar on MS windows has, on windows XP 30px, but on windows 7 is higher
I have the monitor / screen resolution getting by code:
w = self.get_screen()
print w.get_height()
but i still don't have a height of taskbar. Any ideas how to get this height?
On Windows you can use this:
from ctypes import windll, wintypes, byref
SPI_GETWORKAREA = 48
SM_CYSCREEN = 1
def get_taskbar_size():
SystemParametersInfo = windll.user32.SystemParametersInfoA
work_area = wintypes.RECT()
if (SystemParametersInfo(SPI_GETWORKAREA, 0, byref(work_area), 0)):
GetSystemMetrics = windll.user32.GetSystemMetrics
return GetSystemMetrics(SM_CYSCREEN) - work_area.bottom
print get_taskbar_size() # 30
Note that get_taskbar_size will return None if the API call failed.
I try to automatically download a file by clicking on a link on the webpage.
After clicking on the link, I get the 'File Download' Window dialog with 'Open', 'Save' and 'Cancel' buttons. I would like to click the Save button.
I use watsup library in the following way:
from watsup.winGuiAuto import *
optDialog = findTopWindow(wantedText="File Download")
SaveButton = findControl(optDialog,wantedClass="Button", wantedText="Save")
clickButton(SaveButton)
For some reason it does not work. The interesting thing is that exactly the same
code works perfectly to click on 'Cancel' button, however it refuses to work with
'Save' or 'Open'.
Anybody knows what I should do?
Thank you very much,
Sasha
Sasha,
It is highly likely that the file dialog you refer to (the Security Warning file download dialog) will NOT respond to windows messages in this manner, for security reasons. The dialog is specifically designed to respond only to a user physically clicking on the OK button with his mouse. I think you will find that the Run button will not work this way either.
Try this:
from watsup.winGuiAuto import *
optDialog = findTopWindow(wantedText="File Download")
SaveButton = findControl(optDialog, wantedClass="Button", wantedText="Submit")
clickButton(SaveButton)
Sasha,
The code at this link is supposed to work. It uses ctypes instead of watsup.winGuiAuto, and relies on win32 calls. Here is the code:
from ctypes import *
user32 = windll.user32
EnumWindowsProc = WINFUNCTYPE(c_int, c_int, c_int)
def GetHandles(title, parent=None):
'Returns handles to windows with matching titles'
hwnds = []
def EnumCB(hwnd, lparam, match=title.lower(), hwnds=hwnds):
title = c_buffer(' ' * 256)
user32.GetWindowTextA(hwnd, title, 255)
if title.value.lower() == match:
hwnds.append(hwnd)
if parent is not None:
user32.EnumChildWindows(parent, EnumWindowsProc(EnumCB), 0)
else:
user32.EnumWindows(EnumWindowsProc(EnumCB), 0)
return hwnds
Here's an example of calling it to click the Ok button on any window that has
the title "Downloads properties" (most likely there are 0 or 1 such windows):
for handle in GetHandles('Downloads properties'):
for childHandle in GetHandles('ok', handle):
user32.SendMessageA(childHandle, 0x00F5, 0, 0) # 0x00F5 = BM_CLICK
It's possible that the save button is not always enabled. While it may look to your eye that it is, a program might see an initial state that you're missing. Check it's state and wait until it's enabled.
[EDIT] But it's possible that Robert is right and the dialog will just ignore you for security reasons. In this case, I suggest to use BeautifulSoup to parse the HTML, extract the URL and download the file in Python using the urllib2 module.