Python ATOMac Usage and Sample

ATOMac Usage:

Most of the methods in ATOMac works using two well known Mac terminologies

PID: Process ID of an application which can be obtained using 'ps' command in Terminal[For more details on usage of 'ps' command, please visit the man page of the command - "man ps" in Terminal]

BundleId: Bundle Identifier of an application which can be obtained in the property file of any application. Bundle Id usually resides at "/Applications/<App Name>/Content/info.plist" [Eg: Terminal - 'com.apple.Terminal']


Sample:

Lets start with common actions that we do perform during GUI automation.
1. Open an application
2. Get the title of the window
3. Get the buttons present in the window
4. Fetch the attributes of a button
5. Click the button
6. Close the application

Open an application:
>> import atomac 
#Importing ATOMac
>> calc = atomac.getAppRefByBundleId("<Bundle Id>")     
#Launching the application using BundleId. This returns the python object refering the bundle id of the application

Get the title of the window:
>> calc_window = calc.windows()[0]
#Returns the first window of the application
>> calc_title_unicode = calc_window.AXTitle
#Returns the title of the first window in unicode format
>> calc_title = str(calc_title_unicode) 
#Returns the title in string format

Get the buttons present in the window:
>> buttons = calc_window.buttons()     
#Returns the list of buttons present in the window
>> button_name = calc_window.buttons("buttonname")[0]

Fetch the attributes of a button:
>> button_attributes = button_name.getAttributes()     
#Returns the list of attributes that are associated to the button

Click the button:
>> button_pos = button_name.AXPosition     
#Returns the top left most position of the button. Best practice is to find the center position of the button and click it.
>> button_name(clickMouseButtonLeft(button_pos))
#Left click action of the mouse is performed on the object at the mentioned co-ordinates, where 'button_pos' is the co-ordinates

Close the application:
>> atomac.terminateAppRefByBundleId("<Bundle Id>")
#Terminates the application using the bundle identifier

16 comments:

  1. close application is not (terminalAppRefByBundleId), but terminateAppByBundleId

    ReplyDelete
    Replies
    1. Hey Thanks much! Its a typo. Have updated the function.

      Delete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Thanks, nice brief sample of what working with atomac looks like. :)

    ReplyDelete
  4. Hey Ashok, I am very new to atomac..,
    1) If i use "sheet = window.sheets()", I am getting output as []..., I am not able to access any textbox or button may i know what is the issue and how to fix this ?
    2) As you have told above I have used "buttons = window.buttons()" to find the list of buttons in my app and all i got is zoom,minimize and close button there are many other buttons in my app which is not showing up at all

    Thanks in advance

    ReplyDelete
    Replies
    1. Hi Jithendra,
      Apologies for very late reply. Have not been active for sometime.

      The Application UI you are trying to automate might not have been hierarchied with detailed object names. You might have to contact the dev engineer to specify the detailed names of the button the object properties.

      Delete
  5. button_name = calc_window.buttons("buttonname")[0]
    IndexError: list index out of range

    how to fix this?

    ReplyDelete
  6. Hi,
    This means that 'atomac' is not able to identify any buttons in the specified window and the statement "button_name = calc_window.buttons("buttonname")[0]" is returning an empty list. Hence the error.

    ReplyDelete
  7. Your blog has given me that thing which I never expect to get from all over the websites. Nice post guys!

    ReplyDelete
  8. Hi Ashok,

    I see following error while trying to get the elements. can you help me here?

    Error: "AttributeError: 'NoneType' object has no attribute 'windows'"

    ----
    app = atomac.launchAppByBundleId("com.apple.calculator")
    time.sleep(2)

    app_window = your_app.windows()[0]


    ReplyDelete
    Replies
    1. May be the issue here is
      Instead of using "app_window = your_app.windows()[0]", please give a try as "app_window = app.windows()[0]" since the application instance is assigned to 'app'

      Delete
  9. Hi,

    I am very new to Atomac. I want to automate some actions on a gui window that is already launched (not launched by atomac, like autoit works on windows). Is it possible to do in atomac.

    ReplyDelete
    Replies
    1. Yes. This should be possible. We can get the application instance using the "PID" of the process
      Eg;
      import atomac
      calc = atomac.getAppRefByPid() #Consider we automate Calculator app
      calc.findAll()

      Above should return something like below
      [, , ]

      Delete
    2. ***[, , ] - [atomac.AXClasses.NativeUIElement AXMenuBar 'menu bar', atomac.AXClasses.NativeUIElement AXFunctionRowTopLevelElement 'top level function ...', atomac.AXClasses.NativeUIElement AXFunctionRowTopLevelElement 'top level function ...']

      Delete
  10. How do we get buttons present on form which is launched from other form.

    For example Application launches one form, when we click on button on this form it will launch another form where I want to access textbox and buttons of new form....how to do this

    ReplyDelete
  11. Command atomac.terminateAppRefByBundleId("") doesn't work for me
    So I've found another command atomac.terminateAppByBundleId("")
    and all other command:
    >>> dir(atomac)
    ['AXCallbacks', 'AXClasses', 'AXKeyCodeConstants', 'AXKeyboard', 'Clipboard', 'Error', 'ErrorAPIDisabled', 'ErrorCannotComplete', 'ErrorInvalidUIElement', 'ErrorNotImplemented', 'ErrorUnsupported', 'NativeUIElement', 'Prefs', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', '_a11y', 'getAppRefByBundleId', 'getAppRefByLocalizedName', 'getAppRefByPid', 'getFrontmostApp', 'launchAppByBundleId', 'launchAppByBundlePath', 'setSystemWideTimeout', 'standard_library', 'terminateAppByBundleId', 'version']

    ReplyDelete