A problem with downloading a file with Python - python

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.

Related

How can I remove the "Python" text in my plyer notification function?

I am using plyer to send windows notifications, but whenever I do that, there is text saying "Python" under it, is there any way to remove that? I tried using win10toast, but it seems to not cooperate with my tkinter app and causes it to freeze.
from plyer import notification
def download_Complete():
notification.notify(
title = "Youtube Utility",
message = f"Your video, {url_title}, has finished downloading!",
timeout = 10,
app_icon = f"{current_dir}\\images\\main_icon.ico"
)
download_Complete()
You can continue using win10toast itself if your concerned about tkinter freezing. It comes with a threaded option which is by default set to False. Set it to True and you should be good to go.

PyWinAuto trouble clicking the Next Button

This code gets the first window from InstallShield.
from pywinauto import application
from pywinauto import findwindows
app = application.Application()
app.start("MyInstallShieldApp.exe")
time.sleep(15)
hwnd = findwindows.find_windows(title=u"InstallShield Wizard", class_name="MsiDialogCloseClass")
print ("|", str(hwnd), "|")
dlg = app.Window_(handle=hwnd).Wait("enabled", timeout=25, retry_interval=0.5)
Now I want to click the Next button. Swapy says that the Next button has the text '&Next >' and the Button number is 1. But none of these click statements have any effect.
dlg.Click("Next")
dlg.Click(coords=(977, 711))
dlg.Click(button="left")
You misapply Click method. It has the next signatire - Click(button=u'left', pressed=u'', coords=(0, 0), double=False, absolute=False)
To click a button, click should be performed on the button object. So you shoud navigate to the button at first.
In your case the code may look something like:
dlg['&Next >'].Click()
Again, please do not guess, read the docs and see the examples

Keeping the terminal in focus

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")

Finding GUI elements with pywinauto

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")

(no longer seeking answers) Python input box inside a message box

is there any way to have an input box inside of an message box opened with the ctypes library? so far I have:
import ctypes
messageBox = ctypes.windll.user32.MessageBoxA
title = 'Title'
text = 'Message box!'
returnValue = messageBox(None, text, title, 0x40 | 0x1)
print returnValue
and this gives a message box with an image icon and two buttons, both of which I know how to change, and it sets a variable "returnValue" to a number representing the button clicked. However, I also need a variable that will set to a string input in the message box. The reason I need this and I can't just do simple a = raw_input('prompt') is that I want the program itself to run in the background (it would launch itself on logon).
If you want a simple solution, use the PyMsgBox module. It uses Python's built-in tkinter library to create message boxes, including ones that let the user type a response. Install it with pip install pymsgbox.
The documentation is here: https://pymsgbox.readthedocs.org/
The code you want is:
>>> import pymsgbox
>>> returnValue = pymsgbox.prompt('Message box!', 'Title')
Message box is for messages only. What you need is QDialog. You can create it in QtDesigner(I have login dialog created this way, with 2 QLineEdit for username and pass, 2 buttons in QDialogButtonBox and QCombobox for language choose). You'll get .ui file, which you'll need to convert into .py this way in cmd:
pyuic4 -x YourLoginDialogWindow.ui -o YourLoginDialogWindow.py
import created YourLoginDialogWindow.py and you can use it and implement any method you need:
import YourLoginDialogWindow
class YourLoginDialog(QtGui.QDialog):
def __init__(self, parent = None):
super(YourLoginDialog, self).__init__(parent)
self.__ui = YourLoginDialogWindow.Ui_Dialog()
self.__ui.setupUi(self)
...
self.__ui.buttonBox.accepted.connect(self.CheckUserCredentials)
self.__ui.buttonBox.rejected.connect(self.reject)
def GetUsername(self):
return self.__ui.usernameLineEdit.text()
def GetUserPass(self):
return self.__ui.passwordLineEdit.text()
def CheckUserCredentials(self):
#check if user and pass are ok here
#you can use self.GetUsername() and self.GetUserPass() to get them
if THEY_ARE_OK :
self.accept()# this will close dialog and open YourMainProgram in main
else:# message box to inform user that username or password are incorect
QtGui.QMessageBox.about(self,'MESSAGE_APPLICATION_TITLE_STR', 'MESSAGE_WRONG_USERNAM_OR_PASSWORD_STR')
in your __main__ first create login dialog and then your main window...
if __name__ == "__main__":
qtApp = QtGui.QApplication(sys.argv)
loginDlg = YourLoginDialog.YourLoginDialog()
if (not loginDlg.exec_()):
sys.exit(-1)
theApp = YourMainProgram.YourMainProgram( loginDlg.GetUsername(), loginDlg.GetPassword())
qtApp.setActiveWindow(theApp)
theApp.show()
sys.exit(qtApp.exec_())

Categories