Pywinauto and Installshield - installshield

My application is packaged using InstallShield. The first thing that happens when you execute the exe is that IS extracts the msi file; this takes about 10 seconds.
I can't look for my main window until the msi extraction is done, so I use time.sleep. Then I look for and find the handle of my window. But the call to app.window_(handle=1234).Wait("enabled", timeout=25, retry_interval=0.5) blows up with these error messages.
*C:\python32>python test.py
| [2558122] |
Traceback (most recent call last):
File "test.py", line 16, in <module>
dlg = app.Window_(handle=hwnd).Wait("enabled", timeout=25, retry_interval=0.
5)
File "C:\python32\lib\site-packages\pywinauto\application.py", line 380, in Wa
it
WaitUntil(timeout, retry_interval, lambda: self.__check_all_conditions(check
_method_names))
File "C:\python32\lib\site-packages\pywinauto\timings.py", line 292, in WaitUn
til
func_val = func(*args)
File "C:\python32\lib\site-packages\pywinauto\application.py", line 380, in <l
ambda>
WaitUntil(timeout, retry_interval, lambda: self.__check_all_conditions(check
_method_names))
File "C:\python32\lib\site-packages\pywinauto\application.py", line 337, in __
check_all_conditions
check = getattr(self, check_name)
File "C:\python32\lib\site-packages\pywinauto\application.py", line 252, in __
getattr__
ctrls = _resolve_control(self.criteria)
File "C:\python32\lib\site-packages\pywinauto\application.py", line 755, in _r
esolve_control
criteria)
File "C:\python32\lib\site-packages\pywinauto\timings.py", line 356, in WaitUn
tilPasses
func_val = func(*args)
File "C:\python32\lib\site-packages\pywinauto\application.py", line 522, in _g
et_ctrl
findwindows.find_window(**criteria[0]))
File "C:\python32\lib\site-packages\pywinauto\controls\HwndWrapper.py", line 1
80, in __new__
new_class = cls.FindWrapper(handle)
File "C:\python32\lib\site-packages\pywinauto\controls\HwndWrapper.py", line 1
12, in FindWrapper
class_name = handleprops.classname(handle)
File "C:\python32\lib\site-packages\pywinauto\handleprops.py", line 94, in cla
ssname
win32functions.GetClassName (handle, ctypes.byref(class_name), 256)
ctypes.ArgumentError: argument 1: <class 'TypeError'>: Don't know how to convert
parameter 1*
This is the code. I am an experienced programmer but a Python newbie. I have worked on this all day, googled my brains out, and have tried everything I can think of. Thanks in advance.
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)

The problem in hwnd, it is a list, but handle=hwnd requires an int.
You should use find_window instead of find_windows or use only the first handle from hwnd, e.g.
hwnd = findwindows.find_windows(...
dlg = app.Window_(handle=hwnd[0])...
Also it may be useful for you to use SWAPY - UI inspector & code generator for pywinauto.

This question is a year old, but hopefully this will help anyone else in the same position. I initially had something similar to OP and was completely lost because I figured it would be as simple as the 7zip installation example provided in the pywinauto github repo. What I found, however, is that the actual installation opens up in a second process, defeating the point of opening the application with Application().start() to launch and hook onto the program simultaneously.
What I did was I used the Inspect program from the Windows SDK to locate the names of the elements and the window that held them once InstallShield was done extracting the .msi file. Instead of opening the InstallShield executable with start(), I used the subprocess module's popen() function to open the installer and then used Application().connect() to hook into the installer once it was extracted via InstallShield. Below is a rough version of what I did to get this functional, mixing my findings with the examples, documentation, and the OP:
import time
import subprocess
from pywinauto import application
# Open the module via Popen
# I tried using run() first but that prevented the click for reasons unknown
subprocess.Popen(r"C:\Users\fixer446\My Documents\InstallationFiles\Setup.exe")
# Let InstallShield extract the files and the installer load completely
time.sleep(15)
# Hook onto the newly opened setup window
app = application.Application().connect(title="Setup.WindowName", class_name="MsiDialogCloseClass")
# Work with the window and the controls within as advertised in pywinauto
# You may have to play around with this a little
installer = app["Setup.WindowName"]
installer["Next >"].Wait("enabled")
installer["Next >"].Click()
Hope this helps.

app.Window_(...) can take the same arguments as find_windows(...). Yeah, it's not highlighted in the docs now. But the code might be shorter:
from pywinauto import application
from pywinauto import findwindows
app = application.Application()
app.start("MyInstallShieldApp.exe")
# the dialog may not exist yet here
dlg_spec = app.Window_(title=u"InstallShield Wizard", class_name="MsiDialogCloseClass")
# after this line the dialog exists or exception is raised
dlg = dlg_spec.Wait("enabled", timeout=25, retry_interval=0.5)
# dlg is a DialogWrapper object (for really existing dialog)
You may read more detailed description of 2-level WindowSpecification concept in this answer.
I will move it into the docs with coming pywinauto 0.6.0 (a bit later).

Related

How do I make python read a CSV file that has been turned into an excel file?

I was trying to make a program that would read a CSV file that I downloaded from the WorldBank about every countries GDP over time. I expected it to be able to first open the file, then read it, and then retrieve the Data. However, a giant error message appeared (so big in fact, I will place a separation between it and the code), and when I tried to solve those errors by searching answers on this website, there would only be another error. Here is the code. Thank you for anyone willing and able to help out.
import csv
from openpyxl import Workbook
wb = Workbook()
with open(r'C:\Users\Miguel\Downloads\API_NY.GDP.PCAP.CD_DS2_en_csv_v2_1429392.zip') as csvfile:
ws = wb.active
ws.title = "Country Analyzer" #title of the program
ws.sheet_properties.tabColor = "FFFFFF" #RGB values
sheet = wb.get_sheet_by_name('API_NY.GDP.PCAP.CD_DS2_en_csv_v2_1429392')
tuple(sheet['A1':'C3'])
Warning (from warnings module):
File "C:\Users\Miguel\AppData\Local\Programs\Python\Python37-32\I'm so tired of this Work.py", line 14
sheet = wb.get_sheet_by_name('API_NY.GDP.PCAP.CD_DS2_en_csv_v2_1429392')
DeprecationWarning: Call to deprecated function get_sheet_by_name (Use wb[sheetname]).
Traceback (most recent call last):
File "C:\Users\Miguel\AppData\Local\Programs\Python\Python37-32\I'm so tired of this Work.py", line 14, in <module>
sheet = wb.get_sheet_by_name('API_NY.GDP.PCAP.CD_DS2_en_csv_v2_1429392')
File "C:\Users\Miguel\AppData\Local\Programs\Python\Python37-32\lib\site-packages\openpyxl\compat\__init__.py", line 38, in new_func1
return func1(*args, **kwargs)
File "C:\Users\Miguel\AppData\Local\Programs\Python\Python37-32\lib\site-packages\openpyxl\workbook\workbook.py", line 247, in get_sheet_by_name
return self[name]
File "C:\Users\Miguel\AppData\Local\Programs\Python\Python37-32\lib\site-packages\openpyxl\workbook\workbook.py", line 273, in __getitem__
raise KeyError("Worksheet {0} does not exist.".format(key))
KeyError: 'Worksheet API_NY.GDP.PCAP.CD_DS2_en_csv_v2_1429392 does not exist.'
Ps- pls don't judge the name of the file
The file you are trying to open is a zip file. You are going to have to either first unzip the contents or if you want to automate this process, use Python to unzip the file for you - see here or below:
import zipfile
with zipfile.ZipFile(path_to_zip_file, 'r') as zip_ref:
zip_ref.extractall(directory_to_extract_to)
I'd suggest starting by unzipping the file manually before trying to automate the unzip process.
As Nick.McDermaid mentioned, you will then need to determine what format the file is in. to ensure openpyxl can work with it. Per their documentation, "openpyxl is a Python library to read/write Excel 2010 xlsx/xlsm/xltx/xltm files".
If you believe your file is in one of these formats, try this simple code snippet to ensure you can open your file using openpyxl and read from it:
from openpyxl import load_workbook
wb = load_workbook('test.xlsx')
print wb.sheetnames
If that now works, you're ready to continue on with what you were trying to do. Check out the full documentation for reading from a file using openpyxl here: https://openpyxl.readthedocs.io/en/stable/usage.html#read-an-existing-workbook

How to add a new method to an existing import in python? Specifically moviepy

For whatever reason, Python is not allowing me to access a custom method I created in moviepy's preview.py file. I just want to know how to correctly implement it into the file. For reference, before I changed the name of the method, it was working correctly.
I checked at least two __init.py__ files and they were effectively empty. I couldn't find if methods are initialized anywhere, and is probably what I'm missing.
I also tried restarting Git Bash and that didn't work either (another solution I saw).
Original:
#convert_masks_to_RGB
def preview(clip, fps=15, audio=True, audio_fps=22050, audio_buffersize=3000,
audio_nbytes=2, fullscreen=False):
Changed:
#requires_duration
#convert_masks_to_RGB
def preview_custom(clip, marker_overlay="marker_overlay.png", fps=15, audio=True, audio_fps=22050, audio_buffersize=3000,
audio_nbytes=2, fullscreen=False):
There are more than a few differences between the changed and original method, however at the moment the only result I expect is having the method be called correctly. Error is below:
Traceback (most recent call last):
File "T3AJM.py", line 249, in <module>
main()
File "T3AJM.py", line 34, in main
GUI_main_menu()
File "T3AJM.py", line 85, in GUI_main_menu
GUI_play_markers()
File "T3AJM.py", line 125, in GUI_play_markers
video.preview_custom(marker_overlay=TEMP_OVERLAY_FILE)
AttributeError: 'VideoFileClip' object has no attribute 'preview_custom'
Thank you for your time.
I'm not even sure if this technically fixes the problem, but just doing:
from moviepy.video.io.preview import *
and
preview_custom(video, marker_overlay=TEMP_OVERLAY_FILE)
fixed the problem. I have no idea why I had to change the way it was called, as doing clip.preview(), or in this case video.preview() worked perfectly fine before, but whatever.

How to fix Python 3 PyAutoGUI screenshot error? (macOS)

I keep getting an error with any of PyAutoGUI's screenshot taking functions such as:
pyautogui.locateOnScreen('button.png')
pyautogui.pixelMatchesColor(x, y, (r, g, b))
im = pyautogui.screenshot()
The error I get is:
screencapture: cannot write file to intended destination, .screenshot2018-1009_16-43-26-003190.png
Traceback (most recent call last):
File "~/program.py", line 111, in <module>
pyautogui.locateOnScreen('/images/play!.png')
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyscreeze/__init__.py", line 265, in locateOnScreen
screenshotIm = screenshot(region=None) # the locateAll() function must handle cropping to return accurate coordinates, so don't pass a region here.
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyscreeze/__init__.py", line 331, in _screenshot_osx
im = Image.open(tmpFilename)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/PIL/Image.py", line 2609, in open
fp = builtins.open(filename, "rb")
FileNotFoundError: [Errno 2] No such file or directory: '.screenshot2018-1009_16-43-26-003190.png'
I don't tell it to or want it to save the new screenshotted image to any directory (and it shouldn't). With the pyautogui.screenshot() function I could manually save it to a real directory in my project, but I don't have an option to do that with the other methods. Any idea on how to fix this?
What I've tried:
I looked at all the documentation I could find online of pyautogui screenshots
Restarting computer
Downgrading versions for Pillow and pyscreeze
EDIT:
I tried it on another mac and got the same error.
Tried it on windows bootcamp (windows on my mac) and it works fine.
possible, very hack-ish fix - I don't actually like this answer but it was a quick and easy fix (done on OSX with Mojave):
PLEASE NOTE: modifying the source code of libraries you don't understand is usually a bad idea, so do so at your own risk! This worked for me, your milage may vary.
Go to your file (your file path may be different, I just copied this from your error):
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyscreeze/__init__.py
find the line under the function "_screenshot_osx" that looks like
tmpFilename = '.screenshot%s.png' % (datetime.datetime.now().strftime('%Y-%m%d_%H-%M-%S-%f'))
copy it and then comment it out, paste the copied line directly below the commented out original and modify to something like this:
tmpFilename = r'<your preferred screenshot folder here>/screenshot%s.png' % (datetime.datetime.now().strftime('%Y-%m%d_%H-%M-%S-%f'))
save the changes, and see if it works.
Also note: pyautogui.locateOnScreen can be a bit finicky so even if this removes your error you still might not get the coordinates you want (might return none). That might be related to a different issue. To test that part I do this:
import pyautogui
pyautogui.screenshot('testFull.png')
placePos = pyautogui.locateOnScreen('testFull.png')
print(placePos)
even the cursor blinking can mess this up though, and osx has translucent user interfaces so it's kind of annoying to test this perfectly without careful image curation.
I was facing this same issue on MacOS Mojave after changing to Python 3.8.
Here is my solution.
Go the same file mentioned by #Richard W.
There, together with all your 'imports', add the following line so the script can find the tmpFilename folder
dirname = os.path.dirname(__file__)
then, replace the also mentioned line by
tmpFilename = os.path.join(dirname,r'screenshot%s.png' % (datetime.datetime.now().strftime('%Y-%m%d_%H-%M-%S-%f')))

Using COMtypes Python module on different computer throws Exception

Python 3.4.
I am using 32bit. The computer in question also runs microsoft word 32bit.
I am creating a program that reads .MPF files(some sort of Microsoft word document) and parses information.
EDIT:
I was able to re-create the issue using only (3) lines of code, using a test program.
import comtypes.client
print("Creating COMtypes...")
word = comtypes.client.CreateObject('Word.Application')
print("Opening word document...")
doc = word.Documents.Open('C:/1087981_18699245.MPF')
doc.Close()
word.Quit()
print("Test is success!")
I am still getting the same error:
Traceback <most recent call last>:
File "OpenMPFDoc.py", line 4, in <module>
File "C:\Python34x86\lib\site-packages\comtypes\client\__init__.py", line 250, in CreateObject
File "C:\Python34x86\lib\site-packages\comtypes\client\__init__.py", line 188, in _manage
File "C:\Python34x86\lib\site-packages\comtypes\client\__init__.py", line 112, in GetBestInterface
AttributeError: 'module' object as no attribute '_Application'
This program works perfectly on my work computer, but gives this error on the destination computer.

Pyro4, receiving: not enough data

I'm trying to run a function on the server with Pyro4.
Have a look at the code:
==========================Client======================
import Pyro4
def square(x):
return x**2
remoteServer = Pyro4.Proxy('PYRONAME:server')
print(remoteServer.evaluate(square, 4))
===========================Server==========================
import Pyro4
class Server(object):
def evaluate(self, func, args):
return func(*args)
def main():
server = Server()
Pyro4.Daemon.serveSimple({server: "server"},ns=True)
if __name__ == '__main__':
main()
========================Naming Server========================
import Pyro4
Pyro4.naming.startNSloop()
===========================================================
And got an error: "Pyro4.errors.ConnectionClosedError: receiving: not enough data". Look at the full stack trace below:
Traceback (most recent call last):
File "C:\Users\Alex\Desktop\2.py", line 9, in <module>
print(remoteServer.evaluate(square, 4))
File "C:\Users\Alex\AppData\Roaming\Python\Python34\site-packages\Pyro4\core.py", line 169, in __call__
return self.__send(self.__name, args, kwargs)
File "C:\Users\Alex\AppData\Roaming\Python\Python34\site-packages\Pyro4\core.py", line 380, in _pyroInvoke
msg = message.Message.recv(self._pyroConnection, [message.MSG_RESULT], hmac_key=self._pyroHmacKey)
File "C:\Users\Alex\AppData\Roaming\Python\Python34\site-packages\Pyro4\message.py", line 161, in recv
msg = cls.from_header(connection.recv(cls.header_size))
File "C:\Users\Alex\AppData\Roaming\Python\Python34\site-packages\Pyro4\socketutil.py", line 460, in recv
return receiveData(self.sock, size)
File "C:\Users\Alex\AppData\Roaming\Python\Python34\site-packages\Pyro4\socketutil.py", line 195, in receiveData
raise err
Pyro4.errors.ConnectionClosedError: receiving: not enough data
I want the server executes the function that is passed to a method (function).
PS: Windows7, Python 3.4.1
I've found similar question (How to send a function to a remote Pyro object), but didn't work for me.
Tried to use callback decorator #pyro4.callback, but even official example (https://github.com/delmic/Pyro4/tree/master/examples/callback : server2.py, client2.py) didn't work (with python 3.4 and python 2.7) => only message: "server: doing callback 1 to client" is showed and nothing happens.
Thanks
The github repo you're linking to is not the official one. It's a clone (from quite an old version). This is the official repo: https://github.com/irmen/Pyro4
As to your problem: it is not possible to serialize functions and send them over the wire. If you tell Pyro to use pickle as a serialization protocol, you can do more tricks, but pickle still needs to have the module source or bytecodes available on both sides of the connetion.
The reason your client crashes is because the server aborts the connection due to a Pyro protocol error. If you enable the logging it will show up in the server's logfile, something like: "Pyro4.errors.ProtocolError: unsupported serialized class: builtins.function".
Also a tip: you don't have to start the name server using a separate source code file. You can start it directly from the command line.
Lastly, the #pyro4.callback decorator is unrelated, please read http://pythonhosted.org/Pyro4/clientcode.html?highlight=callback#pyro-callbacks for what it is intended for. It influences the way Pyro handles exceptions, nothing more.
edit: note that since Pyro 4.35 this error will be correctly reported back to the client and it will no longer get an aborted connection.

Resources