Sometimes I can not find a window by ParentDlg.child_window() - python

I have just started learning python. Now my work is trying to use pywinauto to do some auto tests. Sometimes I can get one dialog by ParentDlg.child_window(**criterion), sometimes not.
You can think my application(PageManager.exe) has two buttons, [Page1] button and [Page2] button. When application is started, you can click button to start Page.exe process. That means there are two Page.exe at the same time. When Page.exe is started, it starts ImageCalculate.exe. So there are two ImageCalculate.exe two. The parent dialog of ImageCalculate.exe is Page.exe.
When you click [Page1] button, the Page.exe (used for Page1) is displayed and Page.exe (used for Page2) is hid. When you click [Page2] button, the Page.exe (used for Page2) is displayed and Page.exe (used for Page1) is hid.
My problem is when one Page.exe is displayed, such as Page.exe(used for Page1). I can find main dialog of Page.exe by ParentDlg = Desktop(backend="uia").window(best_match="Page1"). After that, I use psutil.pids() and process name "ImageCalculate.exe" to get two PIDs of ImageCalculate.exe, for example, one is 1234, another is 5678. Please attention, two ImageCalculate.exe have almost the same property, same title, class name and so on.
At last I try to get the main dialog of ImageCalculate.exe by ParentDlg.child_window(**criterion).
But according to my test, sometimes I can get main dialog and continue to operate its controls, sometimes not. I don’t know why, I hope someone can help me or provide another way to get the main dialog of ImageCalculate.exe. Because I have the PID of ImageCalculate.exe, maybe I can get the Process’s main dialog by PID? And then maybe I can check this dialog is displayed or not to confirm this dialog belongs to which Page.exe(If this dialog is displayed, I think this dialog belongs to Page1’s Page.exe’s ImageCalculate.exe).
ParentDlg = Desktop(backend="uia").window(best_match="Page1")
// pids contains two pid of ImageCalculate.exe
for pid in pids:
try:
criterion = {}
criterion['class_name_re'] = "AfxFrameOrView*"
criterion['process']=pid
ImageCalculate_dialog = parent.child_window(**criterion)
# I think if ImageCalculate_dialog.writable_props is successful.
# Page1’s Page.exe’s ImageCalculate.exe's main dialog is found.
# But some times ImageCalculate_dialog.writable_props is failed two times and throws ElementNotFoundError
_cname = ImageCalculate_dialog.writable_props
except pywinauto.findwindows.ElementNotFoundError:
continue
else:
return ImageCalculate_dialog
# Now I am trying to use following codes to get main dialog
ImageCalculateApp = Application().connect(process=pid)
dialogs = ImageCalculateApp.windows()
for dialog in dialogs:
print(dialog)
# When I check print result in console
# I can find "hwndwrapper.DialogWrapper - 'ImageCalculate - Untitled',Afx:00400000:b:00010003:00000006:003908A5"
# But I don't know how to use hwndwrapper.DialogWrapper object,
# who could tell me how to use this object or give me some documents about this object?

Related

Identify signal emitted when return key is pressed in a QFileDialog

I'm implementing some customised behaviour for a QFileDialog.
reference_dir = r'D:\My Documents\month'
create_dir_dialog.setDirectory(reference_dir)
create_dir_dialog.setFileMode(create_dir_dialog.Directory)
options = create_dir_dialog.Options(create_dir_dialog.DontUseNativeDialog | create_dir_dialog.ShowDirsOnly)
create_dir_dialog.setOptions(options)
# identify "Choose" button
choose_button = create_dir_dialog.findChild(QtWidgets.QDialogButtonBox).button(
QtWidgets.QDialogButtonBox.Open)
# disconnect "clicked" signal from this button
choose_button.disconnect()
def check_line_edit(path):
# this slot handles enablement of the "Choose" button: if the text
# corresponds to an already-existing directory the button is disabled.
# This is because we are trying to create a new directory.
...
# get the line edit used for the path
lineEdit = create_dir_dialog.findChild(QtWidgets.QLineEdit)
lineEdit.textChanged.connect(check_line_edit)
def create_dir_and_proj():
# this function creates a directory based on the text in the QLE
new_dir = lineEdit.text()
...
create_dir_dialog.close() # programmatically close the dialog
# use the above method as the slot for the clicked signal
choose_button.clicked.connect(create_dir_and_proj)
dlg_result = create_dir_dialog.exec_()
# thread gets held up here
To my delight this works OK.
There's only one fly in the ointment: if, instead of clicking "Choose" with the mouse or using the mnemonic Alt-C to cause the click (both of which cause create_dir_and_proj to run OK), I just press "Return" key when focus is on the dialog, the previous (standard) behaviour occurs, i.e. the behaviour (slot) that I disconnected from the "Choose" button's click signal. This then causes a message box to come up saying "directory does not exist". But the point is that my new slot wants to create the new directory as entered by the user.
I surmise that this is because the "Choose" button is the "default" button of the dialog, and that things have been wired up so that this "Return-key-pressed" signal is wired up to the standard slot as normally used by the "Choose" button.
How do I get hold of this signal with a view to disconnecting it and wiring up a new slot (i.e. the above create_dir_and_proj function)?
That message-box will appear when the line-edit has focus and you press return/enter. The returnPressed signal of the line-edit is connected to the QFileDialog.accept slot. The resulting behaviour will then depend on the FileMode. For the Directory mode, this equates to a request to open the specified directory, which will obviously fail if it doesn't exist.
To override this behviour, you can simply connect your own slot:
lineEdit.returnPressed.disconnect()
lineEdit.returnPressed.connect(create_dir_and_proj)
When the line-edit does not have the focus, pressing return/enter will activate the default button (unless the current focus-widget has built-in behaviour of its own: e.g. navigating to the selected directory in the tree-view). Since you've connected your own slot to the clicked signal of this button, your slot will be called by default.
UPDATE:
It seems that connecting to a Python slot that forms a closure over the dialog can affect the order in which objects get deleted. This may sometimes result in the line-edit completer popup being left without a parent after the dialog closes, which means it won't get deleted and may remain visible on screen. A couple of possible work-arounds are to either explicitly close the popup inside the slot:
def create_dir_and_proj():
lineEdit.completer().popup().hide()
dialog.close()
or disconnect all signals connected to the slot before closing:
def create_dir_and_proj():
choose_button.clicked.disconnect()
lineEdit.returnPressed.disconnect()
dialog.close()
(NB: I have only tested this on Linux; there's no guarantee the behaviour will be the same on other platforms).

No control identifiers using pywinauto

I have a problem with print_control_identifiers() using pywinauto.
I start my test by opening test app.
window1 = Application(backend="uia")
window1.start("C:/Program Files (x86)/.../App.exe")
window1.Dialog.print_control_identifiers()
window1.Dialog.Run.click()
This is ok, I have output with all elements in this dialog - all buttons, boxes, labels etc.
After click() my dialog disappears and i need to connect to new window, so:
window2 = Application().connect(title="new-window")
window2.AppDialog.print_control_identifiers()
And there is my problem: output not contains any elements, just simple:
HwndWrapper[App.exe;App;c002ffc1-d144-4cd1-8ab0-afe5031cb9ea] - 'new-window' (L-8, T-8, R1928, B1058)
['new-window', 'HwndWrapper[App.exe;c002ffc1-d144-4cd1-8ab0-afe5031cb9ea]', 'new-windowHwndWrapper[App.exe;c002ffc1-d144-4cd1-8ab0-afe5031cb9ea]']
child_window(title="new-window", class_name="HwndWrapper[App.exe;c002ffc1-d144-4cd1-8ab0-afe5031cb9ea]")
How to print all control identifiers of this new window?
Whatever I try to find, eg. window2.AppDialog.Login.click() i have pywinauto.findwindows.ElementNotFoundError.
Looks like you forgot to use backend="uia" for Application object in this line:
window2 = Application().connect(title="new-window")
It should look so:
window2 = Application(backend="uia").connect(title="new-window")
If the first window spawns a child process, you have to connect to this process which contains new window. We have plans to implement child process detection by Application object in the future. Now it should be handled manually.
Try this method:
pid= application.process_from_module('toolName.exe')
app= Application(backend="uia").connect(process=pid)
handle = app.window(title_re="new-windowName")
handle.print_control_identifiers()
Or just use Inspect.exe to get all the details if you dont need print it explicitly.
Inspect.exe can get you all the details of any element you focus on..

Python 2.7: Close a dialog without exiting the entire application

I'm running a script coded in python from a scripts menu in a desktop application. It's basically a giant macro that I wrote and added a GUI to. I'm pretty sure the GUI is a really old one that my desktop app uses called dialogKit from MIT.
GitHub still has it here.
The problem is the word "stop" at the very end of the dialog code.
I keep getting a "stop is undefined" message, which I understand, but I've tried everything to close the dialog and if I use exit(), sys.exit(), I don't get an error, but it also closes my entire desktop app.
I need to close the dialog and keep the software open.
The limited dialog documentation for what I'm using can be found here.
(you might have to click on the Dialog section. Their site uses frames.)
class MyDialog:
def __init__(self):
self.d = Dialog(self)
self.d.size = Point(300, 340)
self.d.Center()
self.d.title = "Halftone" #<----- Title of the dialogue
self.d.AddControl(STATICCONTROL, Rect(aIDENT, aIDENT, aIDENT, aIDENT), "frame", STYLE_FRAME)
# more controls and methods..
def on_ok(self, code):
return 1
def on_cancel(self, code):
print "blah"
def Run(self):
return self.d.Run()
d = MyDialog()
if d.Run()!= 1:
stop
I just need a way to change stop to something that 1) will prevent the script from running, and 2) close the dialog without quitting the entire application. This is the functionality of a typical "cancel" button, which is what I want.
Another option is the method called on_cancel(), which I also tried and could get the event itself to work, but still the entire application quits with any kind of exit().
The docs show a method called End(), which claims to terminate the dialog object, but I've tried and failed to get that to work either.
Okay, I'm posting an answer because I think I have a handle on your problem.
Try replacing stop with:
d.d.End()
If that works, you might want to try putting:
self.d.End()
inside of the on_cancel function in your class. That should close the dialogue without closing your program.

How to prevent a double execution of a single-click event when actually trying to trigger a double click event?

I am creating a gtk (pygtk) based GUI file browser on python using IconView. It has two signals - selection-changed which is triggered when an image is selected by single click and item-activated which is triggered when a double-click is performed . I need both the signals in my program , selection-changed to select folders to cut/copy and item-activated to navigate child folders and files.
However, double-clicking generates two selection-changed signals and one item-activated signal. I only need the item-activated signal to be generated.
Hence the question.
The second single-click and the double-click event should arrive simultaniously.
You can use glib.timeout_add(interval, callback, ...) to execute all the single-clicks after a milli second (or increase it).
If within some time a double-click occurred then you do not execute the single-click event.
Implementation hints: use a variable so save when the last double-click occurred and a variable for the last click using time.time().
If the double-click and single-click are very close, do not execute the singe-click code.
Due to the use of the timeout, the single-click always executes after the double-click.
Why not keep track which item is selected? Ignore selection-changed on second click since selection won't have changed. Here is an example from pytk FAQs modified to handle this case...
import gtk
import gtk.gdk
current_path = []
current_path.append(None)
def on_selection_changed(iconview, current_path):
if iconview.get_selected_items():
if cmp(current_path[0], iconview.get_selected_items()[0]):
print "selection-changed"
current_path[0] = iconview.get_selected_items()[0]
def on_item_activated(iconview, path):
print "item-activated"
# First create an iconview
view = gtk.IconView()
view.connect("selection-changed", on_selection_changed, current_path)
view.connect("item-activated", on_item_activated)
# Create a store for our iconview and fill it with stock icons
store = gtk.ListStore(str, gtk.gdk.Pixbuf)
for attr in dir(gtk):
if attr.startswith('STOCK_'):
stock_id = getattr(gtk, attr)
pixbuf = view.render_icon(stock_id,
size=gtk.ICON_SIZE_BUTTON, detail=None)
if pixbuf:
store.append(['gtk.%s' % attr, pixbuf])
# Connect our iconview with our store
view.set_model(store)
# Map store text and pixbuf columns to iconview
view.set_text_column(0)
view.set_pixbuf_column(1)
# Pack our iconview into a scrolled window
swin = gtk.ScrolledWindow()
swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
swin.add_with_viewport(view)
swin.show_all()
# pack the scrolled window into a simple dialog and run it
dialog = gtk.Dialog('IconView Demo')
close = dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_NONE)
dialog.set_default_size(400,400)
dialog.vbox.pack_start(swin)
dialog.run()

How to stop a warning dialog from halting execution of a Python program that's controlling it?

Using Win32GUI and Watsup, I'm writing a bit of Python code to automate a search across a database that is accessed through a program that doesn't come with an interface for it. As such, I can take a string from a list and then input it into the search box and press 'lookup'.
However, when the search returns more than 1000 results, the program throws a warning dialog --which is simply a notification of the number of results--which halts the execution of the Python code. I can't get the code to progress past the line where it presses lookup.
At a guess, this would be because it doesn't expect a window or know how to handle a warning--but I don't either, other than manually accepting it. Below is the relevent sample of code, though it's probably not very enlightening. After "clickButton(LookupButton)", the execution halts.
LookupButtonlocation = elemstring.find("Lookup", AuthNameFieldlocation) - 15
#Use Regex search to find handles
number_regex = re.compile(';(\d+);')
AuthNameEdit = int(number_regex.search(elemstring[AuthNameFieldlocation:]).group(1))
LookupButton = int(number_regex.search(elemstring[LookupButtonlocation:]).group(1))
#Input new Author into Edit Field
setEditText(AuthNameEdit, "John Campbell")
#Click lookup button
clickButton(LookupButton)
I'm not a WATSUP user, but I do something very similar using pywinauto - in my case I'm running a number of automated tests that open various 3rd party programs that, in a similar way, throw up inconvenient warning dialogs. It's a bit difficult to deal with dialogs that you don't know about, however if you do know which dialogs appear, but not when they appear, you can start a thread to just deal with those pop-ups. The following is a simple example from what I'm doing, and uses pywinauto but you could adapt the approach for WATSUP:
import time
import threading
class ClearPopupThread(threading.Thread):
def __init__(self, window_name, button_name, quit_event):
threading.Thread.__init__(self)
self.quit_event = quit_event
self.window_name = window_name
self.button_name = button_name
def run(self):
from pywinauto import application, findwindows
while True:
try:
handles = findwindows.find_windows(title=self.window_name)
except findwindows.WindowNotFoundError:
pass #Just do nothing if the pop-up dialog was not found
else: #The window was found, so click the button
for hwnd in handles:
app = application.Application()
app.Connect(handle=hwnd)
popup = app[self.window_name]
button = getattr(popup, self.button_name)
button.Click()
if self.quit_event.is_set():
break
time.sleep(1) #should help reduce cpu load a little for this thread
Essentially this thread is just an infinite loop that looks for a pop-up window by name, and if it finds it, it clicks on a button to close the window. If you have many pop-up windows you can open one thread per popup (bug that's not overly efficient, though). Because it's an infinite loop, I have the thread looking to see if an event is set, to allow me to stop the thread from my main program. So, in the main program I do something like this:
#Start the thread
quit_event = threading.Event()
mythread = ClearPopupThread('Window Popup Title', 'Yes button', quit_event)
# ...
# My program does it's thing here
# ...
# When my program is done I need to end the thread
quit_event.set()
This is not necessarily the only way to deal with your issue, but is a way that's worked for me. Sorry I can't really help you much with dealing with WATSUP (I always found pywinauto a bit easier to use), but I noticed on the WATSUP homepage (http://www.tizmoi.net/watsup/intro.html), Example 2 does something similar without using threads, i.e., looks for a named window and clicks a specific button on that window.

Categories

Resources