I have to build an automation ui test for a WinForms application. I am using python 3.4 with python for windows extension and pywinauto.
The test is required to access the menu of the application and click on one of the sub menu items.
I used the code below to try and find the menu.
#arrays to store the controls found
classes = []
objects = []
#recursive method to get all the controls
def getClasses(childHwnd, lparam):
objects.append(childHwnd)
classes.append(win32gui.GetWindowText(childHwnd))
return 1
#find handle of the main window
hwnd = win32gui.FindWindow(None, 'Form1')
#get all controls
win32gui.EnumChildWindows(hwnd, getClasses, "a")
# Result:
# objects [1509794, 3344468]
# classes ['Test', 'menuStrip1']
#trying to get the menu of the form
win32gui.GetMenu(hwnd) #Returns 0
Image of the form on which I tested the code above:
As you can see, the menuStrip1 is discovered, but I have not found a way to get to its children (Meniu 1, Meniu 2).
Any idea on how to find the menu and its children?
In the case of Menustrip item an easy workaround is to use the pywinauto basic mouse input modules.
by using this you can move to a specific position of the screen using (x,y) coordinates.
eg:
pywinauto.mouse.click(button='left', coords=(0, 0)) //Click at the specified coordinates
pywinauto.mouse.double_click(button='left', coords=(0, 0)) //Double click at the specified coordinates
pywinauto.mouse.move(coords=(0, 0)) //Move the mouse
pywinauto.mouse.press(button='left', coords=(0, 0)) //Press the mouse button
pywinauto.mouse.release(button='left', coords=(0, 0)) //Release the mouse button
pywinauto.mouse.right_click(coords=(0, 0)) //Right click at the specified coords
pywinauto.mouse.scroll(coords=(0, 0), wheel_dist=1) //Do mouse wheel
pywinauto.mouse.wheel_click(coords=(0, 0)) //Middle mouse button click at the specified coords
After trying without success to use python 3.4 with python for windows extension and pywinauto to create automated tests for the respective project. I looked into other tools, and finally chose one called Sikuli, it's an automation tool based on image recognition of the elements on the screen. It may not be the perfect tool for the job, but it was enough for me.
Other observations about python for windows extension and pywinauto:
can only be used on controls they are aware of (e.g. not
toolstripmenuitem)
it's impossible to test custom controls (for the
reason above)
Observations regarding Sikuli:
all scripts are written in python
it may not use the latest python syntax
at the moment I used it, it had some small bugs
While an answer has already been posted for an alternative solution, I would like to add a workaround that can be used in pywinauto.
On windows, the ALT key can be used to access a menu. For my specific example, I had a settings option "Settings -> Login Remotely". If I wanted to click this menu option I would first have to send ALT+S, and then type L. To do this using pywinauto, assuming you already have a reference to the window, you can do so like this:
myWindow.SetFocus()
myWindow.TypeKeys("%s") #Clicks Settings
myWindow.TypeKeys("l") #Clicks Login Remotely
The '%' character is used to represent the 'ALT' key on your keyboard. Another thing to note here is that if you have multiple menu options beginning with the same letter, you may have to send the key more than once to hit the one you want, and then send the enter key. If there is no ambiguity (in this case, only one submenu option began with L) it was selected automatically.
This requires you to know your menu well, and in what order, but as long as it doesn't change this should work for you.
Related
I'm on Windows OS and I'm trying to perform clicks on background (inactive) applications without bringing them to the front.
I've been successful in doing in in Microsoft Paint (painting a dot on a minimised paint instance) but for some reason this doesn't seem to work on a Java based application (RuneLite.exe) which I'm writing this program for.
Some additional information:
Paint has a lot of child windows and performing the click only seems to work when I execute it on a specific child window (class: Afx:00007FF758890000:8) this is the most inner window.
The RuneLite window structure is as follows:
RuneLite (class: SunAwtFrame)
unknown (class: SunAwtCanvas)
unknown (class SunAwtCanvas)
I've tried using the handle of all 3 of the above windows but none seem to work
The code is able to find the handle of the window and it is correct (checked with Spy++)
The window blinks orange in the taskbar after running the code so some event must have been fired
I have determined the coordinates by screenshotting the window and using paint to find coordinates of a specific location. (If there is a better way of doing this, please let me know)
from pywinauto.application import Application
amount = 2
handles = []
def start_apps():
for i in range(amount):
a = Application()
a.start("D:\\Users\\Arno\\AppData\\Local\\RuneLite\\RuneLite.exe")
time.sleep(15)
handle = a.window().child_window(class_name="SunAwtCanvas", found_index=1).handle # select the most inner window
handles.append(handle)
def do_click(h, x, y):
long_position = win32api.MAKELONG(x, y) # simulate the mouse pointer and send it to the specified coordinates
win32api.SendMessage(h, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, long_position) # simulate mouse press
win32api.SendMessage(h, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, long_position) # Simulate mouse up
start_apps()
print(handles)
time.sleep(20)
for h in handles:
do_click(h, 450, 290)
Output of the code
Does anyone know why this happens and how I could go about fixing it?
I am writing script to add my ssh key to bitbucket's deployment key using selenium. Untill the line
driver.find_element_by_id('add-key').click()
works fine but when a pop up comes, and I want to enter the key in the specific field
driver.find_element_by_name('key').send_keys('testing key added')
It throws this error
Element is not currently visible and so may not be interacted with. I searched on google about this. I come to know that firstly I need to go to this popup window, then I will be able to pass values to respective element. I dont know how to do it.
Please help me how can I focus to the new pop-up window.
I also used time.sleep(10) but still it didn't worked for me.
Problem is driver.find_element_by_name('key').send_keys('testing key added') is giving list of elements. So script need to identify unique element of textarea.
Solution : xpath : (//textarea[#name='key'])[2]
driver.find_element_by_xpath("(//textarea[#name='key'])[2]").send_keys('testing key added')`
Hope You found solution.
Note : If you are getting error randomly , Also Put wait or sleep. This cause minimize the chances of element is not visible(root cause : page is not loaded properly.)
First of all you need to identify the list of windows present, as in your case there are two windows,
parent and new one child. So move the control to child window by specifying name of child Window.(For Verification weither you are on child window or not
add getTitle() Method onces you moved to child window)
as soon as control move on child window, you can enter value for SSH key.(Below code is in java)
// get window ids of all windows
Set<String> windowId = driver.getWindowHandles();
Iterator<String> itererator = windowId.iterator();
String parentWindow = itererator.next();
String childWindow = itererator.next();
driver.switchTo().window(childWindow);
//Enter the value for ssh key,Save and close the child window.
driver.switchTo().window(parentWindow); // now swicth back to the Parent WINDOW.
Thread.sleep(2000);
Hope this will work for you.. :)
I think you not needed to focus to that Pop-up window, please use the Xpath method (find_element_by_xpath) to point that element.
I'm running Python 3.3.3 (and right now I'm on Ubuntu but I also develop on Mac and Windows, which I haven't yet tested). I have a Treeview object that responds to right click on items and shows a context menu depending on what you click... but I've noticed that if you right click somewhere else while the original menu is up, it just opens another one.
In fact, normal clicking doesn't hide them either. Even when I close the window the menus still stay floating. The only way to get them to go away is to click one of the options.
The end result is this:
My code for the menu is as follows:
def rightclick_listitem(self, event):
rowitem = self.sources.identify('item', event.x, event.y)
if rowitem == '':
print('Right clicked an empty space.')
return
# user right clicked something.
self.sources.selection_set(rowitem)
rcmenu = Menu(self.root, tearoff=0)
plugin_disabled=self.sources.item(rowitem, 'values')[0] == 'Disabled'
if plugin_disabled:
rcmenu.add_command(label='Plugin is disabled...',
command=self.plugin_disabled_click)
rcmenu.add_command(label='Plugin options',state='disabled' if plugin_disabled else 'active')
rcmenu.add_command(label='Uninstall plugin')
rcmenu.post(event.x_root, event.y_root)
The code that calls this code is located here:
#RIGHTMOUSE is a variable that changes based on OS due to the way Mac OSX works
#sources is the treeview object
self.sources.bind(RIGHTMOUSE, self.rightclick_listitem)
I googled around and only got some people asking the same question with no answers. I'm still somewhat new to tkinter and python in general, and didn't see anything about this. I bind other actions to the treeview as well.
If you need more sourcecode my project is here: https://github.com/Mgamerz/Fresh-Set-of-Images (freshsetofimages.py)
Any help is appreciated.
And the plugins required to make this appear: https://github.com/Mgamerz/fsoi_plugins
Try calling the method tk_popup rather than post.
Also, your code has a memory leak, in that each time you right-click you're creating a new menu but never destroying the old one. You only ever need to create one, and the reconfigure it before popping it up.
To close the popup menu when click elsewhere, you can add
rcmenu.bind("<FocusOut>",popupFocusOut)
and call unpost in popupFocusOut.
def popupFocusOut(self,event=None):
rcmenu.unpost()
Is there any efficient way using any Python module like PyWind32 to interact with already existing Native OS dialog boxes like 'Save As' boxes?
I tried searching on Google but no help.
EDIT:
1: The Save As dialog box is triggered when user clicks on a Save As dialog box on a web application.
2: Any suggestion are welcome to handle any native OS dialog boxes which are already triggered using Python.(Need not be specific to Selenium webdriver, I am looking for a generic suggestion.)
(When I was posting the question I thought that by 'interacting with a dialog box' will implicitly mean that it is an existing one as if I am able to create one then surely I can interact with it as it is under my programs control. After reading the first 2 answers I realized I was not explicitly clear. Thats why the EDIT )
Thanks
While looking for a possible solution for this I came across several solutions on SO and otherwise.
Some of them were using AutoIT, or editing browsers profile to make it store the file directly without a prompt.
I found all this solution too specific like you can overcome the issue for Save As dialog by editing the browser profile but if later you need to handle some other window then you are stuck.
For using AutoIT is overkill, this directly collides for the fact I choose Python to do this task. (I mean Python is itself so powerful, depending on some other tool is strict NO NO for any Pythonist)
So after a long search for a possible generic solution to this problem which not only serves any one who is looking to handle any Native OS dialog boxes like 'Save As', 'File Upload' etc in the process of automating a web application using selenium web driver but also to any one who wants to interact with a specific window using only Python APIs.
This solution makes use of Win32gui, SendKeys modules of Python.
I will explain first a generic method to get hold of any window desired then a small code addition which will also make this usable while automating a web application using Selenium Webdriver.
Generic Solution::
import win32gui
import re
import SendKeys
class WindowFinder:
"""Class to find and make focus on a particular Native OS dialog/Window """
def __init__ (self):
self._handle = None
def find_window(self, class_name, window_name = None):
"""Pass a window class name & window name directly if known to get the window """
self._handle = win32gui.FindWindow(class_name, window_name)
def _window_enum_callback(self, hwnd, wildcard):
'''Call back func which checks each open window and matches the name of window using reg ex'''
if re.match(wildcard, str(win32gui.GetWindowText(hwnd))) != None:
self._handle = hwnd
def find_window_wildcard(self, wildcard):
""" This function takes a string as input and calls EnumWindows to enumerate through all open windows """
self._handle = None
win32gui.EnumWindows(self._window_enum_callback, wildcard)
def set_foreground(self):
"""Get the focus on the desired open window"""
win32gui.SetForegroundWindow(self._handle)
win = WindowFinder()
win.find_window_wildcard(".*Save As.*")
win.set_foreground()
path = "D:\\File.txt" #Path of the file you want to Save
ent = "{ENTER}" #Enter key stroke.
SendKeys.SendKeys(path) #Use SendKeys to send path string to Save As dialog
SendKeys.SendKeys(ent) #Use SendKeys to send ENTER key stroke to Save As dialog
To use this code you need to provide a string which is the name of the window you want to get which in this case is 'Save As'. So similarly you can provide any name and get that window focused.
Once you have the focus of the desired window then you can use SendKeys module to send key strokes to the window which in this case includes sending file path where you want to save the file and ENTER.
Specific to Selenium Webdriver::
The above specified code segment can be used to handle native OS dialog boxes which are triggered through a web application during the automation using Selenium Webdriver with the addition of little bit of code.
The issue you will face which I faced while using this code is that once your automation code clicks on any Web Element which triggers a native OS dialog window, the control will stuck at that point waiting for any action on the native OS dialog window. So basically you are stuck at this point.
The work around is to generate a new thread using Python threading module and use it to click on the Web Element to trigger the native OS dialog box and your parent thread will be moving on normally to find the window using the code I showed above.
#Assume that at this point you are on the page where you need to click on a Web Element to trigger native OS window/dialog box
def _action_on_trigger_element(_element):
_element.click()
trigger_element = driver.find_element_by_id('ID of the Web Element which triggers the window')
th = threading.Thread(target = _action_on_trigger_element, args = [trigger_element]) #Thread is created here to call private func to click on Save button
th.start() #Thread starts execution here
time.sleep(1) #Simple Thread Synchronization handle this case.
#Call WindowFinder Class
win = WindowFinder()
win.find_window_wildcard(".*Save As.*")
win.set_foreground()
path = "D:\\File.txt" #Path of the file you want to Save
ent = "{ENTER}" #Enter key stroke.
SendKeys.SendKeys(path) #Use SendKeys to send path string to Save As dialog
SendKeys.SendKeys(ent) #Use SendKeys to send ENTER key stroke to Save As dialog
#At this point the native OS window is interacted with and closed after passing a key stroke ENTER.
# Go forward with what ever your Automation code is doing after this point
NOTE::
When using the above code in automating a web application, check the name of the window you want to find and pass that to find_window_wildcard(). The name of windows are browser dependent. E.g. A window which is triggered when you click on an Element to Upload a file is called 'File Upload' in Firefox and Open in Chrome.
Uses Python2.7
I hope this will help any one who is looking for a similar solution whether to use it in any generic form or in automating a web application.
EDIT:
If you are trying to run your code through command line arguments then try to use the thread to find the window using Win32gui and use the original program thread to click on the element (which is clicked here using the thread). The reason being the urllib library will throw an error while creating a new connection using the thread.)
References::
SO Question
SenKeys
Win32gui
There is a Python module called win32ui. Its found in the Python for Windows extensions package. You want the CreateFileDialog function.
Documentation
Edit:
This is a save dialog example. Check the documentation for the other settings.
import win32ui
if __name__ == "__main__":
select_dlg = win32ui.CreateFileDialog(0, ".txt", "default_name", 0, "TXT Files (*.txt)|*.txt|All Files (*.*)|*.*|")
select_dlg.DoModal()
selected_file = select_dlg.GetPathName()
print selected_file
from time import sleep
from pywinauto import Application, keyboard
app = Application(backend='uia').start('notepad.exe')
app.top_window().set_focus()
main = app.window(title='Untitled - Notepad', control_type='Window')
main.draw_outline(colour='red', thickness=2)
fileMenu = main.child_window(title='File')
fileMenu.draw_outline(colour='red', thickness=2)
fileMenu.click_input()
keyboard.send_keys("{VK_CONTROL}{VK_SHIFT}{S}")
sleep(2)
dlg = main.window(title='Save As')
dlg.draw_outline(colour='red', thickness=2)
savebtn = dlg.child_window(title='Save')
filePath = dlg.child_window(title='File name:', control_type='Edit')
savebtn.draw_outline(colour='red', thickness=2)
filePath.set_text('D:\\aa.txt')
filePath.draw_outline(colour='red', thickness=2)
savebtn.click_input()
I recommend using pywinauto lib.
If you have opened notepad and "Save as" dialog window:
from pywinauto import application
app = application.Application()
app.connect(title='test1.txt - Notepad')
app.SaveAs.Edit1.type_keys("test2.txt")
app.SaveAs.Save.click()
I was wondering how I could use wxPython along with the win32apis to create a simple script that will activate a window (if it is not already active) with a certain title and output text (keystrokes). One possible application for this would be keyboard shortcuts in gaming. I have read up on the wxPython RegisterHotKey(), but- as an amateur Python programmer- it is unclear to me.
The basic structure of the script will be:
Define the hotkey (something like win+F_)
Watch for the hotkey keystroke
See if the desired window (title) is already active, and activate it if it isn't
Simulate the typing of some text
I know there are simpler methods to accomplish this (such as AutoHotkey), but I feel more comfortable using something I have written myself and have taken an interest in Python.
Thanks!
For the record, I am using Python 2.7 on Windows 7 AMD64, though I doubt that the interpreter version/platform/architecture makes much of a difference here.
Are you talking about activating a window that you created in wx or a separate application, like notepad? If it's with wx, then it's trivial. You'd just use Raise() to bring whatever frame you need into focus. You would probably use PubSub or PostEvent to let the sub-frame know that it needs to Raise.
If you're talking about notepad, then things get much stickier. Here's an ugly hack I created based on some stuff I got from various locations on the web and the PyWin32 mailing list:
def windowEnumerationHandler(self, hwnd, resultList):
'''
This is a handler to be passed to win32gui.EnumWindows() to generate
a list of (window handle, window text) tuples.
'''
resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
def bringToFront(self, windowText):
'''
Method to look for an open window that has a title that
matches the passed in text. If found, it will proceed to
attempt to make that window the Foreground Window.
'''
secondsPassed = 0
while secondsPassed <= 5:
# sleep one second to give the window time to appear
wx.Sleep(1)
print 'bringing to front'
topWindows = []
# pass in an empty list to be filled
# somehow this call returns the list with the same variable name
win32gui.EnumWindows(self.windowEnumerationHandler, topWindows)
print len(topWindows)
# loop through windows and find the one we want
for i in topWindows:
if windowText in i[1]:
print i[1]
win32gui.ShowWindow(i[0],5)
win32gui.SetForegroundWindow(i[0])
# loop for 5-10 seconds, then break or raise
handle = win32gui.GetForegroundWindow()
if windowText in win32gui.GetWindowText(handle):
break
else:
# increment counter and loop again
secondsPassed += 1
Then I used the SendKeys package to send text to the window (see http://www.rutherfurd.net/python/sendkeys/). If the user opens anything else, the script will break or weird things will happen. If you open something like MS Office, use win32com instead of SendKeys. That's much more reliable.