Searching for Specific Elements - asmagill/hammerspoon GitHub Wiki
This document is currently just a snapshot of notes and examples being made for discussing hs.axuielement:elementSearch. This is the heart of searching for specific elements within an application and as such has a lot of options available. In here I plan to discuss some of the ways this method can be used and even expanded upon.
Because of the power and complexity of this method, I am also in the process of determining what additional support methods might be added that can simplify some of the more common cases, including a way to more specifically direct searches rather than the breadth first search currently used.
In the mean time, you are welcome to examine this and make use of what you can; however, expect it to be incomplete and subject to change until it is more formalized.
It should also be noted that some of the examples here may require some of the bug fixes or additions that are currently in https://github.com/Hammerspoon/hammerspoon/pull/2554, which will hopefully be included in Hammerspoon when 0.9.82 is released.
ax = require("hs.axuielement")
inspect = require("hs.inspect")
finspect = function(...) return inspect({ ... }, { newline = " ", indent = "" }) end
s = ax.applicationElement("Safari")
r1 = s:buildTree(function(msg, r) r2 = r ; print(msg) end)
> r1:matched(), r1:visited(), r1:runTime()
5700 5700 199
> r1[1] == r2
true
> inspect(r2)
<1>{
AXChildren = { <2>{
AXAuditIssues = {
_code = -25212,
error = "Requested value does not exist"
},
AXCancelButton = {
_code = -25212,
error = "Requested value does not exist"
},
AXChildren = { <3>{
AXChildren = { <4>{
AXAuditIssues = {
_code = -25212,
error = "Requested value does not exist"
},
AXFrame = {
h = 890.0,
w = 1.0,
x = 8.0,
y = 111.0
},
... cut for brevity ...
_actions = {},
_attributes = { "AXFunctionRowTopLevelElements", "AXChildren", "AXFocusedUIElement", "AXFrontmost", "AXRole", "AXExtrasMenuBar", "AXMainWindow", "AXFocusedWindow", "AXTitle", "AXChildrenInNavigationOrder", "AXEnhancedUserInterface", "AXRoleDescription", "AXHidden", "AXMenuBar", "AXWindows" },
_element = <userdata 6303> -- hs.axuielement: AXApplication (0x6060002722d8),
_parameterizedAttributes = { "AXReplaceRangeWithText" }
}
(286460 lines)
> s[1].AXRole
AXWindow
> s[2].AXRole
AXMenuBar
> r3 = s[1]:buildTree(function(msg, r) r4 = r ; print(msg) end)
completed
> r3:matched(), r3:visited(), r3:runTime()
330 330 6
> inspect(r4) -- not shown for brevity -- it's still 19010 lines
> links1 = s[1]:elementSearch(function(msg, r, c) print(msg, c) ; links2 = r ; end, ax.searchCriteriaFunction({ attribute = "AXRole", value = "AXLink" }))
completed 41
> links1:matched(), links1:visited(), links1:runTime(), links1 == links2
41 330 5 true
> for i,v in ipairs(links1) do print(v.AXURL.url) end
https://about.google/?fg=1&utm_source=google-US&utm_medium=referral&utm_campaign=hp-header
https://store.google.com/US?utm_source=hp_header&utm_medium=google_ooo&utm_campaign=GS100042&hl=en-US
... cut for brevity ...
https://podcasts.google.com/
https://www.google.com/intl/en/about/products?tab=wh
-- for the Hammerspoon github page:
> s1 = s:childrenWithRole("AXWindow")[1]
> r1 = s1:elementSearch(function(...) print(...) end, hs.axuielement.searchCriteriaFunction({ attribute = "AXRole", value = "AXLink" }))
completed hs.axuielement.elementSearchObject (0x60600091c8b8) 155
> r1:runTime()
18
> for i,v in ipairs(r1) do print(i, v.AXTitle, v.AXURL.url) end
1 Skip to content https://github.com/Hammerspoon/hammerspoon#start-of-content
2 https://github.com/
3 https://github.com/pulls
... cut for brevity...
153 Training https://services.github.com/
154 Blog https://github.blog/
155 About https://github.com/about
> a = {} ; for i,v in ipairs(r1) do table.insert(a, hs.canvas.new(v.AXFrame):show():appendElements{ type = "rectangle", action = "stroke", strokeColor = { red = 1 } }) end
> for i,v in ipairs(a) do v:delete() end
> r1 = s1:elementSearch(function(...) print(...) end, hs.axuielement.searchCriteriaFunction("AXLink"))
completed hs.axuielement.elementSearchObject (0x606002d8a538) 155
> r2 = s1:elementSearch(function(...) print(...) end, hs.axuielement.searchCriteriaFunction({ { attribute = "AXRole", value = "AXLink" }, { attribute = "AXURL", value = { url = "asmagill" }, pattern = true } }))
completed hs.axuielement.elementSearchObject (0x606002d8a538) 0
> r2 = s1:elementSearch(function(...) print(...) end, hs.axuielement.searchCriteriaFunction({ { attribute = "AXURL", value = { url = "asmagill" }, pattern = true } }))
completed hs.axuielement.elementSearchObject (0x606002d8a538) 0
> r2 = s1:elementSearch(function(...) print(...) end, hs.axuielement.searchCriteriaFunction({ { attribute = "AXRole", value = "AXLink" }, { attribute = "AXURL", value = { { url = "asmagill" } }, pattern = true } }))
completed hs.axuielement.elementSearchObject (0x606002d8a538) 3
> r3 = r1:filter(hs.axuielement.searchCriteriaFunction({ { attribute = "AXURL", value = { { url = "asmagill" } }, pattern = true } }), function(...) print(...) end)
completed hs.axuielement.filterObject (0x606002d8a538)
> #r3
3
> r1:runTime(), r2:runTime(), r3:runTime()
18 18 1
> for i,v in ipairs(r3) do print(i, v.AXTitle, v.AXURL.url) end
1 @asmagill https://github.com/asmagill
2 asmagill https://github.com/Hammerspoon/hammerspoon/commits?author=asmagill
3 @asmagill https://github.com/asmagill
> for i,v in ipairs(r2) do print(i, v.AXTitle, v.AXURL.url) end
1 @asmagill https://github.com/asmagill
2 asmagill https://github.com/Hammerspoon/hammerspoon/commits?author=asmagill
3 @asmagill https://github.com/asmagill
> r4 = s:elementSearch(function(...) print(...) end, hs.axuielement.searchCriteriaFunction({ { attribute = "AXRole", value = "AXWindow"}, { attribute = "AXFrame", value = { { y = 23 } } } }), { depth = 3 })
completed hs.axuielement.elementSearchObject (0x606001a32678) 2
> for i,v in ipairs(r4) do print(v.AXTitle, finspect(v.AXFrame)) end
Hammerspoon/hammerspoon: Staggeringly powerful macOS desktop automation with Lua { h = 978.0, w = 1192.0, x = 9.0, y = 23.0 }
error - How do you get system diagnostic files from macOS? - Ask Different { h = 978.0, w = 1192.0, x = 30.0, y = 23.0 }
> r5 = s:elementSearch(function(...) print(...) end, hs.axuielement.searchCriteriaFunction({ { attribute = "AXRole", value = "AXWindow"}, { attribute = "AXFrame", value = { { y = 23 } }, comparison = ">" } }), { depth = 3 })
completed hs.axuielement.elementSearchObject (0x606001a32678) 2
> for i,v in ipairs(r5) do print(v.AXTitle, finspect(v.AXFrame)) end
DuckDuckGo — Privacy, simplified. { h = 978.0, w = 1192.0, x = 360.0, y = 94.0 }
DuckDuckGo — Privacy, simplified. { h = 978.0, w = 1192.0, x = 220.0, y = 223.0 }
> r6 = s:elementSearch(function(m, r, c) print(m,c, r:runTime(), (c > 0) and r[#r].AXTitle or "end") end, hs.axuielement.searchCriteriaFunction("AXWindow"), { count = 1, depth = 3 })
countReached 1 0 Hammerspoon/hammerspoon: Staggeringly powerful macOS desktop automation with Lua
> r6:next()
hs.axuielement.elementSearchObject (0x60600478b6b8)
countReached 1 0 DuckDuckGo — Privacy, simplified.
> r6:next()
hs.axuielement.elementSearchObject (0x60600478b6b8)
countReached 1 0 DuckDuckGo — Privacy, simplified.
> r6:next()
hs.axuielement.elementSearchObject (0x60600478b6b8)
countReached 1 0 error - How do you get system diagnostic files from macOS? - Ask Different
> r6:next()
hs.axuielement.elementSearchObject (0x60600478b6b8)
completed 0 3 end