-
Notifications
You must be signed in to change notification settings - Fork 1
/
UIexplorer.py
157 lines (126 loc) · 3.63 KB
/
UIexplorer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import subprocess
def asrun(ascript):
osa = subprocess.Popen(['osascript', '-'], stdin = subprocess.PIPE,
stdout = subprocess.PIPE, universal_newlines=True)
return osa.communicate(ascript)[0]
class codeBuilder:
def build_code(self):
code = '\n'.join(self.header + self.footer)
self.last_code = code
return code
def add_snippet(self,head,foot=''):
self.header.append(head)
if len(foot) >0: self.footer.insert(0,foot)
def __repr__(self):
return self.myself
def __str__(self):
#list of children
ret = []
for key,value in self.properties.items():
ret.append('{}: {}'.format(key,value))
return '\n'.join(ret)
def __init__(self, myself, parent = None, process_name='Word'):
self.process_name = process_name
self.myself = myself
self.parent = parent
self.children = []
self.properties = {}
self.myselfstring = self.getMySelfStr()
# print (self.myselfstring)
self.reset_code()
def getMySelfStr(self):
myselfstring = self.myself
if self.parent:
myselfstring = "{} of {}".format(myselfstring, self.parent.getMySelfStr())
return myselfstring
def reset_code(self):
self.header = []
self.footer = []
self.bring_process()
def bring_process(self):
self.add_snippet(
'''tell application "System Events" to tell process "{}"'''.format(self.process_name),
'end tell'
)
def details(self):
if len(self.properties): return
self.add_snippet('properties of {}'.format(self.myselfstring))
ret = asrun(self.build_code()).split(':')
self.header.pop()
# properties parsing
key = ret[0]
for x in ret[1:]:
xsp = x.split(',')
value = ', '.join([ y.strip() for y in xsp[:-1] ]).strip()
exc_prop = [
'minimum value'
'orientation',
'position',
'size',
'role',
'subrole'
]
if key not in exc_prop and value != "missing value" and value !="":
# print('{}: {}'.format(key, value))
self.properties[key] = value
key = xsp[-1].strip()
def updateName(self):
# print(self.myselfstring)
self.details()
self.myself = '{} "{}"'.format(self.properties['class'], self.properties['name'])
self.myselfstring = self.getMySelfStr()
def getChildren(self):
self.add_snippet('ui elements of {}'.format(self.myselfstring))
ret = asrun(self.build_code())
self.header.pop()
# ui children parsing
uis = [r.strip() for r in [r.split('of')[0] for r in ret.split(',')]]
return uis
def add_child(self, ui):
self.children.append( codeBuilder(ui, self, self.process_name) )
return self.children[-1]
def build_tree(self,depth = 1):
if len(self.children)==0:
if depth == 0: return
if depth <0: depth = -1
for n,ui in enumerate(self.getChildren()):
if ui=='':
continue
if ui.split()[-1].isnumeric():
self.add_child(ui).build_tree( depth - 1 )
else:
ch = self.add_child('ui element {}'.format(n+1))
ch.updateName()
ch.build_tree( depth - 1 )
return len(self.children)
def run(self):
ret = asrun(self.build_code())
return ret
# below example is to navigate UI hierarchy of ribbon tab area of MS Office
if __name__ == '__main__':
C = codeBuilder('tab group 1 of window 1')
while 1:
print()
print(C.myself)
print('---------------------------------------')
C.details()
print(C)
print('---------------------------------------')
hasChildren = C.build_tree(1)
if hasChildren:
for i, child in enumerate(C.children):
print(i, child.myself)
print('b: go to parent')
inp = input()
if inp.isnumeric():
no = int(inp)
C = C.children[int(no)]
else:
if inp == 'b':
if C.parent: C = C.parent
if inp == 'q':
break
if inp == 'c':
print(C.last_code)
if inp == 'p':
print(C.myselfstring)