How to get window application status in python - python

I'm currently writing a piece of code to test windows app based on pyautowin.
When of the test is to check if we can minimized the window.
Below is the code:
MyApp.Start_(bittorrentApp)
time.sleep(2)
w_handle = pywinauto.findwindows.find_windows(title=u'Bittorrent Automation Task', class_name='WindowsForms10.Window.8.app.0.2bf8098_r15_ad1')[0]
window = MyApp.window_(handle=w_handle)
window.Click()
window.ClickInput(coords = (300,10))
time.sleep(1)
lStyles = win32api.GetWindowLong(GWL_STYLE);
if( lStyles & WS_MINIMIZE ):
print "minimized"
else:
print "not minimized"
I have imported win32api and I can minimized the window.
By the way
lStyles = win32api.GetWindowLong(GWL_STYLE);
return an error, saying GWL_STYLE is not defined
Any idea ?

pywinauto already has all such functionality.
if window.HasStyle(pywinauto.win32defines.WS_MINIMIZE):
window.Minimize()
That's all in HwndWrapper class. You can see all its attributes when typing window.WrapperObject(). in popup hint. WrapperObject() call is usually hidden for readability, but it's called implicitly anyway.
BTW, GetWindowLong(handle, style) has 2 parameters.

Related

Pywinauto unable to find/close pop-up window

Source code
def is_admin():
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
if is_admin():
app = Application(backend='uia').start("C:\\Program Files (x86)\\Advantech\\AdamApax.NET Utility\\Program\\AdamNET.exe")
win = app['Advantech Adam/Apax .NET Utility (Win32) Version 2.05.11 (B19)']
win.wait('ready')
win.menu_select("Setup->Refresh Serial and Ethernet")
win.top_window().print_control_identifiers(filename="file.txt")
# win.top_window().OKButton.click_input() ---------This is what I hope to do
else
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1)
Problem Statement
I had to run this application with elevation rights. The above is my code. The problem is I can't identify the window (view in output image) that pops up after selection from menu. I need to close the window. Please excuse the line
win.top_window().print_control_identifiers(filename="file.txt")
It was meant write the identifiers into a text file because the structure of this code does not display the outputs for me to view. However, since nothing is appended, I guess pywinauto couldn't identify the dialog.
For a clearer understanding, please view the image (input) of when it selects the menu.
Input
Now, it pops up with this dialog (output)
Output
I've also used spy to identify the caption and it gives:
(Handle: 004E07D4,
Caption: Information,
Class: #32770(Dialog),
Style: 94C801C5)
Other things I've tried:
Besides using win.topwindow() to identify the dialog, I've used
win[Information].OKButton.click_input()
win[Information].OK.click_input()
win[Information].OK.close()
win[Information].OK.kill(soft=false)
win.Information.OKButton.click_input()
win.Information.OK.click_input()
win.Information.OK.close()
win.Information.OK.kill(soft=false)
app[Information] ...... curious if I could discover the new window from original application
I've also send keys like enter, space, esc & alt-f4 to close the dialog with libraries like keyboard, pynput & ctypes. It still doesn't work.
Link to download the same application: http://downloadt.advantech.com/download/downloadsr.aspx?File_Id=1-1NHAMZX
Any help would be greatly appreciated !
I finally found a thread that demonstrated the way multi thread works to solve this issue. I tried it myself and it works. It's a little different as a few parts of the code have depreciated. Here is the link to the solution:
How to stop a warning dialog from halting execution of a Python program that's controlling it?
Here are the edits I made to solve the problem:
def is_admin():
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
if is_admin():
def __init__(self, window_name, quit_event):
threading.Thread.__init__(self)
self.quit_event = quit_event
self.window_name = window_name
def run(self):
while True:
try:
handles = windows.find_windows(title=self.window_name)
except windows.WindowNotFoundError:
pass
else:
for hwnd in handles:
app = Application()
app.connect(handle=hwnd)
popup = app[self.window_name]
popup.close()
if self.quit_event.is_set():
break
time.sleep(1)
quit_event = threading.Event()
mythread = ClearPopupThread('Information', quit_event)
mythread.start()
application = Application(backend="uia").start("C:\\Program Files (x86)\\Advantech\\AdamApax.NET Utility\\Program\\AdamNET.exe")
time.sleep(2)
win = application['Advantech Adam/Apax .NET Utility (Win32) Version 2.05.11 (B19)']
win.menu_select("Setup->Refresh Serial and Ethernet")
quit_event.set()
else:
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1)
The best thing is this solution works for every other dialog that halts the main script from working & I could use them to do different actions like clicking buttons, inserting values, by adding more multi threads.

PyQt: mainWindow closed, but process still running

I'm writing a Python application with PyQt 5.10.It seems I have some sort of bug/memory leak, since when I call close() on my MainWindow the process keeps running. After a bit of research and debugging I was able to circumscribe the supposedly faulty code.This is my main:
if __name__ == '__main__':
app = QApplication(sys.argv)
matteo = God()
matteo.runApp()
sys.exit(app.exec())
Here you will find the runApp function from God class:
def runApp(self):
self.painter = Painter()
self.dbManager = DBManager()
self.userInput = UserInput()
self.excelFile = ExcelFile()
self.painter.connectToClasses(self, ["god","db","ui"])
self.excelFile.connectToClasses(self, ["god",])
self.painter.drawMainWindow()
self.loadConf()
self.openDB(True)
if self.dbManager.error==None:
self.painter.drawSearchWidget()
else:
print("Closed.")
The process keeps running when the app is not able to find the configuration file, and so it creates a new one from scratch and it asks the user to select the database which he wants to connect to. This prompts an error message - if the selected file is corrupted or it's not the right format - and I think there might lie my problem.That's the code:
def checkError(self, classType):
if classType=="db":
error = self.dbManager.error
elif classType=="excel":
error = self.excelFile.error
if error!=None:
self.painter.drawError(classType)
self.userInput.error = self.painter.error.clickedButton()
self.userInput.error = self.painter.error.buttonRole(self.userInput.error)
if (self.userInput.error==1):
self.painter.mainWindow.close()
return 0
return 1
def drawError(self, classType):
if (classType=="db"):
title = "Database"
error = self.dbManager.error
otherButton = "Browse"
elif (classType=="excel"):
title = "Excel file"
error = self.excelFile.error
try:
self.setErrorText(False, error)
if error[0]:
if self.error.icon()!=3:
self.error.setIcon(3)
buttons = self.error.buttons()
for button in buttons:
if button.text()!="Quit":
button.hide()
button.deleteLater()
except AttributeError:
self.error = QMessageBox()
self.setErrorText(True, error)
if (error[0]):
self.error.setIcon(3)
else:
self.error.setIcon(2)
self.error.addButton(otherButton, self.error.AcceptRole)
self.error.addButton("Quit", self.error.RejectRole)
self.error.setWindowTitle(title)
self.error.exec()
If the user clicks the quit button on the error window, the function closes main window and return 0. The other functions return and the application prints closed on the console. But the process keeps going.
I kind of resolved this issue calling sys.exit() instead of printing closed.
It's a brute force solution to say the least, but I needed to deliver the application fast and I had no time to debug it.I guess this sad story will end here.

How to bring a window into focus if it does not have a title?

We have an app that is built with openframeworks. When started, it first opens a console window that does some work (and stays open) and then starts two more child processes that each open a window in fullscreen, one on each monitor. According to the guy that is building the app, it is impossible to give those two windows titles.
My job is to build a script that:
Checks if the app has crashed and reopens it
Verifies that the windows are in the foreground and one of them is in focus and fixes them if they aren't
I want to reuse an old python script of mine that did exactly this and altered it to fit the bill.
from time import sleep
import subprocess
import psutil
import re
import win32gui
import win32con
client_path = "C:\\path_to_app.exe"
window_name = ""
class WindowMgr:
"""Encapsulates some calls to the winapi for window management"""
def __init__(self, ):
"""Constructor"""
self._handle = None
def find_window(self, class_name, window_name=None):
"""find a window by its class_name"""
self._handle = win32gui.FindWindow(class_name, window_name)
def _window_enum_callback(self, hwnd, wildcard):
'''Pass to win32gui.EnumWindows() to check all the opened windows'''
if re.match(wildcard, str(win32gui.GetWindowText(hwnd))) is not None:
self._handle = hwnd
def find_window_wildcard(self, wildcard):
self._handle = None
win32gui.EnumWindows(self._window_enum_callback, wildcard)
def set_foreground(self):
"""put the window in the foreground"""
win32gui.SetForegroundWindow(self._handle)
def maximize(self):
win32gui.ShowWindow(self._handle, win32con.SW_MAXIMIZE)
def is_minimized(self):
return win32gui.IsIconic(self._handle)
def client_crashed():
for pid in psutil.pids():
if psutil.Process(pid).name() == "app.exe":
return False
return True
if __name__ == "__main__":
w = WindowMgr()
w.find_window_wildcard(window_name)
print("Checking")
while True:
if client_crashed() is True:
print("Reopening app.exe")
subprocess.Popen([client_path])
else:
print("Not crashed")
if w.is_minimized:
print("Maximizing")
w.set_foreground()
w.maximize()
else:
print("Not minimized")
print("Sleeping for 10")
sleep(10)
Now the checking for crashing and restarting works just fine. But since the windows have no title, the best I've come up with so far is to check for windows with no name, which apparently opens random programms like the Windows 10 movie programm (or at least brings them to the foreground which is weird because they should not be running).
Is there a better way to bring a window into focus without knowing its name? One thought of mine was to get the parent process and then access the children from there and bring them into focus somehow, but I've not been able to figure out how.
If there are better ways to achieve what I want than using python, I would also be glad for any pointers in that direction.
If you have unique window class name you can try GetClassName() https://msdn.microsoft.com/en-us/library/windows/desktop/ms633582(v=vs.85).aspx
Or get the window process GetWindowThreadProcessId() and check whether it's an instance of your app. See How to get the process name in C++
One thought of mine was to get the parent process and then
access the children from there and bring them into focus somehow, but
I've not been able to figure out how."
I've been through the very similar issue, I wanted to find a child proccess with random title name, I only had the name of the executable. Here is what I've done.
import win32gui
def windowFocusPassingPID(pid):
def callback(hwnd, list_to_append):
list_to_append.append((hwnd, win32gui.GetWindowText(hwnd)))
window_list = []
win32gui.EnumWindows(callback, window_list)
for i in window_list:
print(i) # if you want to check each item
if pid == i[0]:
print(i) # printing the found item (id, window_title)
win32gui.ShowWindow(i[0], 5)
win32gui.SetForegroundWindow(i[0])
break
# Here we will call the function, providing a valid PID
windowFocusPassingPID(INSERT_PID_HERE_IT_MUST_BE_AN_INTEGER)
So, this is a function that will call win32gui.EnumWindows, which handles all top-level windows. Those windows will be appended to the window_list, letting us know all the ID's and window names...
In the loop, we will iterate over the list and match the PID you want to bring to focus...
You can comment both print(i) if you want.
Here is the documentation:
http://timgolden.me.uk/pywin32-docs/win32gui__EnumWindows_meth.html

Cancel Excel's close event using python and win32com

Currently I try to cancel Excel's close event using Python and win32com. I have already managed to handle this issue with IronPython some month ago. But for further purposes of my companies department this should also be able with Python. Followed you will see two snippets. The first will contain the working IronPython Code
import clr
clr.AddReference("Microsoft.Office.Interop.Excel")
clr.AddReference("System.Windows.Forms")
from Microsoft.Office.Interop import Excel
from System.Windows.Forms import Form, Application, MessageBox, MessageBoxButtons, MessageBoxIcon, DialogResult
class CloseEventTry(Form):
def __init__(self):
excel = Excel.ApplicationClass()
excel.Visible = True
excel.DisplayAlerts = False
self.workbooks = excel.Workbooks.Add()
self.Text = "Dummy GUI Window"
#link "BeforeCloseEvent" to the "beforeClose" method
self.workbooks.BeforeClose +=Excel.WorkbookEvents_BeforeCloseEventHandler(self.beforeClose)
def beforeClose(self, cancel):
print type(cancel) #Type: 'StrongBox[bool]
choice = MessageBox.Show("Close Excel", "Close", MessageBoxButtons.YesNo, MessageBoxIcon.Information)
if choice == DialogResult.Yes:
cancel.Value = False #do't cancel the close action
self.Close()
elif choice == DialogResult.No:
cancel.Value = True #prevent excel from closing
Application.Run(CloseEventTry())
The second one will contain the version with Python and win32com. This one is based on my IronPython snippet and the sample of that link
https://win32com.goermezer.de/microsoft/office/events-in-microsoft-word-and-excel.html
import clr
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import Form, Application, MessageBox, MessageBoxButtons, MessageBoxIcon, DialogResult
import win32com.client as win32
#workbook event handler class. Needed according to this example https://win32com.goermezer.de/microsoft/office/events-in-microsoft-word-and-excel.html
class WorkBookEvents(object):
def OnBeforeClose(self, cancel):
print(type(cancel)) #Type: class 'bool'
choice = MessageBox.Show("Close Excel", "Close", MessageBoxButtons.YesNo, MessageBoxIcon.Information)
if choice == DialogResult.Yes:
#do't cancel the close action => raises AttributeError: 'bool' object has no attribute 'Value' Exception
cancel.Value = False
self.Close()
elif choice == DialogResult.No:
#prevent excel from closing => raises AttributeError: 'bool' object has no attribute 'Value' Exception
cancel.Value = True
class CloseEventTry(Form):
def __init__(self):
excel = win32.DispatchEx('Excel.Application')
excel.Visible = True # makes the Excel application visible to the user
excel.DisplayAlerts = False
self.Text = "Dummy GUI Window"
self.workbooks = excel.Workbooks.Add()
#define event handler according to this example https://win32com.goermezer.de/microsoft/office/events-in-microsoft-word-and-excel.html
self.workbooks = win32.DispatchWithEvents(self.workbooks, WorkBookEvents)
Application.Run(CloseEventTry())
As you will see I could connect to the "OnBeforeClose" event, but cannot cancel the close event as I've done it with the IronPython version. As mentioned in the last code snippet's comment, the Python version raises an AttributeError exception. Further you can also see, that the types of the needed "cancel" variable of the event handlers have two different types. In the IronPython version its a "StrongBox[bool]". On the other hand the Python version's type is a common "class 'bool'" type (which explains the exception). Thats way I tried to just type
cancel = True #prevent excel from closing
But using this way, excel closes anyway.
I also did some research but was not able to find a solution for this issue. My Assumption is that there is some kind of wrapper needed?
you can achieve the same behaviour as the IronPython version by returning the desired cancel value, e.g.
return True
in case you want to abort the Close event.
Regards

Program doesn't work completely when run outside eclipse [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 11 years ago.
I have a small python application, which uses pyttsx for some text to speech.
How it works:
simply say whatever is there in the clipboard.
The program works as expected inside eclipse. But if run on cmd.exe it only works partly if the text on the clipboard is too large(a few paras). Why ?
when run from cmd, it prints statements , but the actual 'talking' doesn't work(if the clipboard text is too large
Here is a of the program part which actually does the talking: As can be seen the 'talking' part is handled inside a thread.
def saythread(queue , text , pauselocation, startingPoint):
saythread.pauselocation = pauselocation
saythread.pause = 0
saythread.engine = pyttsx.init()
saythread.pausequeue1 = False
def onWord(name, location, length):
saythread.pausequeue1 = queue.get(False)
saythread.pause = location
saythread.pauselocation.append(location)
if saythread.pausequeue1 == True :
saythread.engine.stop()
def onFinishUtterance(name, completed):
if completed == True:
os._exit(0)
def engineRun():
if len(saythread.pauselocation) == 1:
rate = saythread.engine.getProperty('rate')
print rate
saythread.engine.setProperty('rate', rate-30)
textMod = text[startingPoint:]
saythread.engine.say(text[startingPoint:])
token = saythread.engine.connect("started-word" , onWord )
saythread.engine.connect("finished-utterance" , onFinishUtterance )
saythread.engine.startLoop(True)
engineRun()
if saythread.pausequeue1 == False:
os._exit(1)
def runNewThread(wordsToSay, startingPoint):
global queue, pauselocation
e1 = (queue, wordsToSay, pauselocation, startingPoint)
t1 = threading.Thread(target=saythread,args=e1)
t1.start()
#wordsToSay = CLIPBOARD CONTENTS
runNewThread(wordsToSay,0)
Thanks
Edit: I have checked than the python version used is the same 2.7 . The command used to run the program in cmd : python d:\python\play\speech\speechplay.py
Checked that the problem is not in the code that reads the text from the clipboard.
You should check if your eclipse setup specifies custom environment variables for the project which do not exist outside Eclipse. Especially:
PYTHONPATH (and also additional projects on which your program could depend in your setup)
PATH
Use
import os
print os.environ['PATH']
print os.environ['PYTHONPATH']
at the beginning of your program to compare both settings.
Misc stylistic advices:
don't use os._exit, prefer sys.exit (you should only use os._exit in a child process after a call to os.fork, which is not available on Windows)
I think a threading.Event would be more appropriate than a queue.Queue
I'd use a subclass approach for the thread with methods rather than a function with inner functions
For example:
import threading
import sys
import pyttsx
class SayThread(threading.Thread):
def __init__(self, queue, text, pauselocation, startingPoint, debug=False):
threading.Thread.__init__(self)
self.queue = queue
self.text = text
self.pauselocation = pauselocation
self.startingPoint = startingPoint
self.pause = 0
self.engine = pyttsx.init(debug=debug)
self.pausequeue1 = False
def run(self):
if len(self.pauselocation) == 1:
rate = self.engine.getProperty('rate')
print rate
self.engine.setProperty('rate', rate-30)
textMod = self.text[self.startingPoint:]
self.engine.say(self.text[self.startingPoint:])
self.engine.connect("started-word", self.onWord )
self.engine.connect("finished-utterance", self.onFinishUtterance )
self.engine.startLoop(True)
if self.pausequeue1 == False:
sys.exit(1)
def onWord(self, name, location, length):
self.pausequeue1 = self.queue.get(False)
self.pause = location
self.pauselocation.append(location)
if self.pausequeue1 == True :
self.engine.stop()
def onFinishUtterance(self, name, completed):
if completed == True:
sys.exit(0)
def runNewThread(wordsToSay, startingPoint):
global queue, pauselocation
t1 = SayThread(queue, wordsToSay,
pauselocation, startingPoint)
t1.start()
#wordsToSay = CLIPBOARD CONTENTS
runNewThread(wordsToSay,0)
In fact, eclipse itself uses a commandline command to start it's apps.
You should check what command eclipse is giving to start the program. It might be a bit verbose, but you can start from there and test what is necessary and what isn't.
You can find out the commandline eclipse uses by running the program and then selecting the output in the debug window. Right-click it, select properties and you're done.
If you don't have a debug window you can open it window/show view/(other possibly)/debug.
turns out pythonpath wasn't set properly on my system.
Edit: turns out pythonpath isn't the problem. I have no idea whats the problem. arghhhhhhhhhhhhhhhhhhhhhhhh

Categories

Resources