pywinauto find_elements() returns ElementNotFoundError - python

I'm trying to automate a simple app using pywinauto & Python 3.6. The app has a Windows "Open" dialog like this and I want to click on the "Cancel" button:
I used SWAPY to get the class_name and control_id attributes for the button.
Now the problem is that when I call the find_element() method with these parameters it raises an ElementNotFoundError. Here's my code:
cancel_button = pywinauto.findwindows.find_element(class_name="button", control_id=2)
I've tried (class_name="button", control_id="2"), (class_name="Button", control_id=2) but they all give the same error. The same problem occurs for any other element I try to find on this dialog.
So how do I use the attributes read from SWAPY? I didn't find the official pywinauto documentation to be very useful. It doesn't explain a lot of things clearly.
EDIT: I decided not to use the find_elements method and instead used find_windows() to get a handle to the Open dialog.
w_open_handle = pywinauto.findwindows.find_windows(title=u'Open', class_name='#32770')[0]
I then get a WindowSpecification object using this handle:
w_open = app.window_(handle=w_open_handle)
I then call:
w_open['Cancel'].click()
and this works. Now I want to enter a file name in the "File name:" edit box and click on Open button to open that file. So I do this:
w_open['File name:'].type_keys("abc.txt")
This works. I printed out the control identifiers using print_control_identifiers() and got the name for Open button. So using draw_outline() I draw a boundary outside it and it shows the correct button.
w_open['SplitButton6'].draw_outline()
But calling .click() method on 'SplitButton6' throws a WindowSpecification class has no 'click' method error. Any idea what's causing this? The error seems to be misleading since WindowSpec class does have a .click method.

Correct answer is that you missed top_level_only=False (it's True by default because higher level API calls it at least twice). Then you may have 2 controls matching this criterion (maybe from different applications). find_element is a low level function. I wouldn't recommend its direct usage (the code is too long, there are many pitfalls that were taken into account on a higher level API).
>>> pywinauto.findwindows.find_element(class_name="Button", control_id=2, top_level_only=False)
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
File "...\pywinauto\findwindows.py", line 98, in find_element
raise exception
ElementAmbiguousError: There are 2 elements that match the criteria {'class_name': 'Button', 'control_id': 2, 'top_level_only': False}
>>> pywinauto.findwindows.find_element(class_name="Button", title='Cancel', top_level_only=False)
<win32_element_info.HwndElementInfo - 'Cancel', Button, 395554>
Using higher level API (Application object and WindowSpecifications described in the Guide) you shouldn't care about passing process id, backend name and other things to find_element every time.
P.S. In my mind SWAPY could be significantly improved, but it's not maintained last year. I hope to re-write it in the future with smaller code base and MS UI Automation support. But currently fully automatic script generator is a higher priority.
EDIT:
This button w_open['SplitButton6'].draw_outline() could be detected as general HwndWrapper object instead of ButtonWrapper. You can check it using this:
w_open['SplitButton6'].wrapper_object()
And this is what exactly written in the Getting Started Guide (which you said you've read).
Fortunately you can use method .click_input() for any control:
w_open['SplitButton6'].click_input()
I can say more: WindowSpecification does NOT have click method. It's a method of ButtonWrapper which is instantiated dynamically. For example these statements work the same way (but Python can hide .wrapper_object() call):
w_open['SplitButton6'].wrapper_object().click_input()
w_open['SplitButton6'].click_input()
And again this is all described in the Getting Started Guide. Please read the whole guide. You will find many useful high level things. I can advise for some corner cases if something is still not clear.

Related

Invalid flag "testVisibility" in Autodesk Maya 2016

I'm trying to determine if a set of mesh nodes is currently visible or not. Using pymel and isVisible() works, but it's a huge performance hit.
Looking through the documentation, I found something that would (hopefully) solve my problem, the flag testVisibility on the Command hide.
According to the documentation the flag command will return a value that tells me if the specified nodes are visible or not.
The problem is, that flag doesn't exist.
import maya.cmds as cmds
cmds.sphere(name='testsphere')
cmds.hide('testsphere', testVisibility = True)
Gives an error
# Error: Invalid flag 'testVisibility'
# Traceback (most recent call last):
# File "<maya console>", line 4, in <module>
# TypeError: Invalid flag 'testVisibility' #
Same thing with the shortname of the flag "tv", as well as doing the whole thing in MEL: hide -testVisibility;.
The documentation includes this flag since Maya 2016. This is also the Maya version that I am using currently (more specifically, Maya 2016 SP5). Using the built-in link to the python documentation I arrive at the same documentation as I posted above. Looking through the changelog of SP6 doesn't mention anything of it either, so I assume it won't solve my problem.
I tried the same command on Maya 2017 and it works. That won't help me much though because that's not the Maya version that our team uses currently.
I can't contact Autodesk support because I'm not a subscriber (thanks autodesk, great help).
So my questions are:
is there something that I'm missing/that I overlooked? Are these flags only implemented in some kind of super duper developer plugin? Do Maya versions only use the API of the previous versions or something?
is there a way to check if a command has a flag, without try-catching it?
any workarounds that are not as performance draining as the pymel route I mentioned above?
You're right. I also tried tv flag for hide command and it definitely doesn't work.
Try setter/getter:
import maya.cmds as cmds
cmds.sphere(name="testsphere")
cmds.setAttr("testsphere.visibility", False)
cmds.getAttr("testsphere.visibility")
I tested it in Maya 2016/2018.
# Result: False #

Sublime Text accessing view file name error

I am new to Python, and Sublime Text plugin development, and I don't know what I'm doing wrong here. I am using Sublime Text 3. I'm trying to create a plugin that will copy the file name to the clipboard. Can anyone help me understand this python error and/or offer a solution?
import sublime, sublime_plugin
class Filename_to_clipboardCommand(sublime_plugin.TextCommand):
def run(self, edit):
sublime.set_clipboard(sublime.View.file_name())
sublime.message_dialog("The full file path was copied to the clipboard")
and the error, when I call the plugin from the console, is:
>>> view.run_command('filename_to_clipboard')
Traceback (most recent call last):
File "/Applications/Sublime Text.app/Contents/MacOS/sublime_plugin.py", line 549, in run_
return self.run(edit)
File "/Users/ivan/Library/Application Support/Sublime Text 3/Packages/Filename_to_clipboard/filename_to_clipboard.py", line 5, in run
sublime.set_clipboard(sublime.View.file_name())
TypeError: file_name() missing 1 required positional argument: 'self'
When I do:
sublime.set_clipboard(view.file_name())
from the conosole, it works! Why?
Try self.view.file_name() rather than sublime.View.file_name(). You have a reference to an instance of the view for your TextCommand. It was written for ST2, but you may want to take a look at this tutorial
http://net.tutsplus.com/tutorials/python-tutorials/how-to-create-a-sublime-text-2-plugin/
I don't know anything about the Sublime Text API, but it looks like your problem here is that file_name() is an instance method, and sublime.View is a class so sublime.View.file_name() fails.
In Python, when you call an instance method you are implicitly passing the instance as the first argument, which is why it says you are missing the argument self. You could even write the call explicitly. For example, view.file_name() could also be written sublime.View.file_name(view), but that's kind of silly.
Good luck with your plugin :)
I hate to see all your effort wasted, but this functionality already exists in ST3. If you right-click in the edit area, Copy File Path is one of the options on the context menu. To create a keyboard shortcut, open Preferences -> Key Bindings - User and add the following item:
{ "keys": ["super+i"], "command": "copy_path" }
You can of course change the key binding to whatever you want. If the key bindings file is empty when you open it, just add opening and closing square brackets:
[
{ "keys": ["super+i"], "command": "copy_path" }
]
However, I still encourage you to learn Python and plugin programming, they're both quite rewarding and great fun! Good luck!
EDIT
Based on a comment by #skuroda, here's how I discovered the command to use:
I already knew there was a Copy File Path option in the context menu, but looking through Preferences -> Key Bindings - Default I couldn't find any shortcuts or macros already assigned to that action, and I didn't know exactly what the command's name was. So, I hit Ctrl` to open the console, then ran
sublime.log_commands(True)
to have all actions logged to the console. I then right-clicked and selected Copy File Path, and
command: copy_path
showed up, along with a message regarding the mouse's location when the context menu event occurred. I assigned the key combination, ran it, and it worked. To finish up, I run
sublime.log_commands(False)
so the console doesn't get clogged with unnecessary info, then hit Ctrl` again to close the console.

Calling functions with arguments on "command" and "bind"

i want to point, that i am learning python since short time.
The question is going be to beginner one.
I need to add command to menu at top of program, which would call function "color_picker("red").
kolory.add_command(label="Czerwony", command=color_picker('red'))
When i am using that, its somehow wrong, cuz its called once the program started, its not waiting for me to click the menu button. (i am sure of it, as i added "showinfo" to that function, and it shows the message before i do anything)
kolory.add_command(label="Czerwony", command=lambda: color_picker('red')) That one kinda works, but i don't know what does "lambda" mean here. Is it only way to call functions with arguments under menu options?
Same question goes to binding keyboard shortcuts.
okno.bind("1", color_picker) - that will call the function but does not have the argument, which should be a color. How can i do that?
So, how to assign functions WITH arguments, to keyboard shortcuts and to menu using add_command?
PS. Was searching trough google, but it seems python does not have so good documentation like c# does for example. Or i am too stupid to find it.
EDIT:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1410, in __call__
return self.func(*args)
TypeError: color_picker() takes at most 1 argument (2 given)
Thats the error message, when i try to use "pick_red" in okno.bind
I'm not sure if I understand the question, but here goes;
The problem is that you are calling the color_picker function (by adding () after the function name).
What you want to do is pass the actual function, not the result of the function call as the command keyword argument, e.g. add_command(label="Czerwony", command=color_picker)
However, since you want to give it a fixed argument 'red', you must use partial from functools, something like;
from functools import partial
pick_red = partial(color_picker, "red")
kolory.add_command(label="Czerwony", command=pick_red)
EDIT:
Now that your error message shows that you are using Tkinter, we can see that according to documentation the function that is given to bind() is always passed an event parameter, so you need a function that can accept it;
def pick_red_with_event(event):
# We really do nothing with event for now but we always get it...
color_picker("red")
okno.bind("1", pick_red_with_event)
Same thing works for okno.bind, if you have defined pick_red as above, just do:
okno.bind("1", pick_red)

Eclipse Pydev: Supress no-self errors in python wrappers generated with swig

when generating python wrappers with swig the python wrapper classes in the generated python file do not have an explicit self parameter, for example see below:
class PySwigIterator(_object):
def value(*args): return _spatiotemporalnmf.PySwigIterator_value(*args)
def incr(*args): return _spatiotemporalnmf.PySwigIterator_incr(*args)
def decr(*args): return _spatiotemporalnmf.PySwigIterator_decr(*args)
def distance(*args): return _spatiotemporalnmf.PySwigIterator_distance(*args)
I am developing with the eclipse pluging Pydev. Pydev always shows an error when it detects a method without explicit self parameter. I am aware of two methods to get rid of the errors: First, disable error checking for the whole project in the Pydev preferences. Second, add a ##NoSelf to every line with an error. I don't want to use the first one, because I still want to get error warnings for my non-swig-generated files. Obviously the second one is also not very good, because I would have to do it by hand and every time I generate the file again, all ##NoSelfs will be gone.
My Question now is, is there a better way to achieve this?
Thanks
As from the documentation, any file with the comment
##PydevCodeAnalysisIgnore
inside will not be analyzed.
Therefore, you just need to add it to all SWIG-generated files, and you should be OK. It is just one place to change, and you could even write a very small processor that will add it automatically.

"Unknown object" error when trying to capture artwork from a pict file and embed it into a track

I'm trying to capture artwork from a pict file and embed it into a track on iTunes using python appscript.
I did something like this:
imFile = open('/Users/kartikaiyer/temp.pict','r')
data = imFile.read()
it = app('iTunes')
sel = it.current_track.get()
sel.artworks[0].data_.set(data[513:])
I get an error OSERROR: -1731
MESSAGE: Unknown object
Similar applescript code looks like this:
tell application "iTunes"
set the_artwork to read (POSIX file "/Users/kartikaiyer/temp.pict") from 513 as picture
set data of artwork 1 of current track to the_artwork
end tell
I tried using ASTranslate but it never instantiates the_artwork and then throws an error when there is a reference to the_artwork.
This is an older question, but since I was having trouble doing this same thing now, I thought I'd post my solution in case someone else might benefit.
selected = appscript.app('iTunes').selection.get()
for t in selected:
myArt = open(/path/to/image.jpg,'r')
data = myArt.read()
t.artworks[1].data_.set(data) # no need to remove header but one-indexed as has said earlier
myArt.close()
Hope this helps.
At a quick guess, Appscript references, like AppleScript references, use 1-indexing, not zero-indexing like Python lists. So you probably need to write:
it.current_track.artworks[1].data_.set(...)
(Incidentally, the extra get command in your original script is unnecessary, though harmless in this case.)
As for ASTranslate, you need to enable the 'Send events to app' checkbox if you want it to actually send commands to applications and scripting additions and receive their results. As a rule, it's best to disable this option so that you don't have any unfortunate accidents when translating potentially destructive commands such as set or delete, so only to enable it if you really need it, and be careful what code you run when you do.
The read command is part of Scripting Additions, which ASTranslate doesn't translate to. Use ASDictionary to create a glue for Scripting Additions, by clicking "Choose Installed Scripting Additions" Under the Dictionary menu, and then selecting "Scripting Additions" from the list.

Categories

Resources