Which is the best way to interact with already open native OS dialog boxes like (Save AS) using Python?
Asked Answered
B

4

6

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

Bette answered 21/6, 2013 at 12:27 Comment(14)
What sort of interaction are you trying to do?Seitz
@DavidHeffernan I have an web application with triggers a new Save as Dialog box. I need to interact in the sense that I need to click on different options present on the dialog and enter destination path and file name to be saved.Bette
Is this to automate testing of a web app? Will the script be running in the back round waiting for the popup? Or is it part of the web app?Mexican
@Mexican Yes this is automation of a web app. The script is running independentlyBette
Is this not the same question as this?Norris
@Norris Sort of but the solutions talk about using AutoIt or Editing your browsers profile to facilitate to save the file. I am looking for a more generic answer by which I can handle any native OS dialog boxes like browse files , Upload etc.Bette
Well, it's almost certainly possible, but it'd be a complete pain in the butt, and very OS- and case-specific. If the "Save As" is your only use-case, I'd suggest using a simpler alternative.Norris
@Norris AutoIT solution did not look that good to me , there should be some solution where I can use windowsAPi to do that.) I have come across some, experimenting with them, will post a solution if all works good.Bette
@Bette I was thinking more along the lines of a solution which avoids using the "Save As" and just downloads automatically. If the objective is to test your web application, it seems unnecessary to test the web browser itself.Norris
@Norris In my application I come across many native OS dialog boxes like Upload file, browse file etc. So that is why I am looking for a generic solution. Maybe in future if I need to do some thing similar which does not have web browser in the story then I should be able to do it. I have found the solution and it is working fine. I will post the solution in some shortly.Bette
@Bette Fair enough. Would be interested to see your solution. The one I had in mind was insanely complicated. :)Norris
@Norris Please see the solution I posted. Any feedback will be great. Also I would love to read the solution you had in your mind.Bette
@Bette +1 for your answer. I would've done something similar except just using the ctypes module to hit the Win32 API directly. I also would've limited the search for the correct window handle to only those owned by the browser process, in case you have some other application open which has an active "Save As" dialog. However, I'd probably never use something like that in practice. If I wanted to test stuff like uploading/downloading, I'd do it with something like mechanize, assuming it supports such things, otherwise rolled my own with httplib or somesuch.Norris
@Norris Thanks for suggesting mechanize I will have a look at it. Thanks again for the suggestions. httplib hint gave me a new direction. Will work on that. :)Bette
B
10

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

Bette answered 27/6, 2013 at 18:16 Comment(3)
Unfortunately SendKeys it is no longer reliable...Fae
is there a way to get this code updated according to the new libraries available in python 3.12?thanksLemberg
@Lemberg What do you mean? You can always do that?Bette
U
3

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
Unravel answered 21/6, 2013 at 12:57 Comment(1)
I do not need to create a New Save As dialog box but to interact with an already existing one. Can you suggest some approach for that?Bette
P
0

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()
Parse answered 8/7, 2022 at 9:20 Comment(0)
P
-1

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()
Pistol answered 11/6, 2022 at 21:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.