Programmatically iterate through all the controls located in a window

I am looking for some pointers/code snippets to use to retrieve the following information through scripting:

  • list of all the controls located in a window
  • determine the type of control; example: text box, label, template etc
  • if the control is template, determine the template type and path
  • retrieve value of one of the properties of a template
    i would like to run this code snipped in the visionwindow opened event.

Start with something like this:

r = event.source.rootContainer for comp in r.components: print comp.name, comp.getClass()
Watch the debug console to see what is going on. You’ll want to make this recursive to get everything in groups and containers, and with known types, you can selectively access all the normal properties of these components.

2 Likes

Here it is in 2022 and this post just helped me, so I thought I would share what I wrote to accomplish this. I am running v8.1.18 at this time but I don’t think that matters.

I put this code on a button on a window I want to loop through and run it.

Notes:

  1. You’ll see some "if 1 == 1:"s in there. That’s so you can drop in a try…except…if you wish and the indentation level stays correct.
  2. In the function you’ll see a couple of tasks I wanted to perform…I checked if a component had a tootip defined and I set all components to visible
  3. The depth at which each component is found is tracked, so you could filter your actions based on that.
  4. There is something unusual going on with this script though. Even on a window with relatively few components, this script will only print to the console once. The second time I click the button, nothing gets printed to the console, but the window does not appear to be locked up. If anyone has any insight into that, that would be greatly appreciated.

If anyone wishes to comment on my coding style, feel free. I deliberately made this a bit more “verbose” than befits Python because eventually someone a lot less experienced than I am will be looking at this code.

def lvlTasks(lv,cn,comp,llen,cListAll):
	print '\r\n'
	print '\t' * (lv-1) + 'Level: %s' % (lv)
	print '\t' * (lv-1) + 'Number of components at this level: %s' % (llen)
	print '\t' * (lv-1) +  'Level %s - Component %s of %s    Component Name %s:   Component type: %s:' % (lv,cn,llen,comp.name,type(comp))
	if comp.name is None:
		nme = 'None'
	else:
		nme = str(comp.name)
	cListAll.append(nme + ' at Level %s' % (lv))
	try:
		print '\t' * (lv-1) +  'Mouseover Text is: %s' % (comp.toolTipText)
	except:
		pass
	try:
		comp.visible = 1
	except:
		pass
	return cListAll

win = system.gui.getParentWindow(event)

cmpList1 = win.getComponents()
lvl = 0
idx = 0
jdx = 0
kdx = 0
ldx = 0
mdx = 0
ndx = 0
odx = 0
pdx = 0
qdx = 0
lv1CmpCnt = 0
lv2CmpCnt = 0
lv3CmpCnt = 0
lv4CmpCnt = 0
lv5CmpCnt = 0
lv6CmpCnt = 0
lv7CmpCnt = 0
lv8CmpCnt = 0
lv9CmpCnt = 0
cListAll = []
for cmpLv1 in cmpList1:
	lvl += 1
	lv1CmpCnt += 1
	idx += 1
	print "\r\n\r\nLevel 1\r\nNumber of components at this level: %s" % (len(cmpList1))
	print "Level 1 - Component %s of %s    Component Name %s:   Component type: %s:" % (idx,len(cmpList1),cmpLv1.name,type(cmpLv1))
#	try:
	if 1 == 1:
		lvl += 1
		cmpList2 = cmpLv1.getComponents()
		jdx = 0
		for cmpLv2 in cmpList2:
			jdx += 1
			lv2CmpCnt += 1
			cListAll = lvlTasks(lvl,jdx,cmpLv2,len(cmpList2),cListAll)
			if 1 == 1:
				lvl += 1
				cmpList3 = cmpLv2.getComponents()
				kdx = 0
				for cmpLv3 in cmpList3:
					kdx += 1
					lv3CmpCnt += 1
					cListAll = lvlTasks(lvl,kdx,cmpLv3,len(cmpList3),cListAll)
#					try:
					if 1 == 1:
						lvl += 1
						cmpList4 = cmpLv3.getComponents()
						ldx = 0
						for cmpLv4 in cmpList4:
							ldx += 1
							lv4CmpCnt += 1
							cListAll = lvlTasks(lvl,ldx,cmpLv4,len(cmpList4),cListAll)
							if 1==1:
#							try:
								lvl += 1
								cmpList5 = cmpLv4.getComponents()
								mdx = 0
								for cmpLv5 in cmpList5:
									mdx += 1
									lv5CmpCnt += 1
									cListAll = lvlTasks(lvl,mdx,cmpLv5,len(cmpList5),cListAll)
#									try:
									if 1==1:	
										lvl += 1
										cmpList6 = cmpLv5.getComponents()
										ndx = 0
										for cmpLv6 in cmpList6:
											ndx += 1
											lv6CmpCnt += 1
											cListAll = lvlTasks(lvl,ndx,cmpLv6,len(cmpList6),cListAll)
											if 1 == 1:
												lvl += 1
												cmpList7 = cmpLv6.getComponents()
												odx = 0
												for cmpLv7 in cmpList7:
													odx += 1
													lv7CmpCnt += 1
													cListAll = lvlTasks(lvl,odx,cmpLv7,len(cmpList7),cListAll)


													if 1 == 1:
														lvl += 1
														cmpList8 = cmpLv7.getComponents()
														pdx = 0
														for cmpLv8 in cmpList8:
															pdx += 1
															lv8CmpCnt += 1
															cListAll = lvlTasks(lvl,pdx,cmpLv8,len(cmpList8),cListAll)

															if 1 == 1:
																lvl += 1
																cmpList9 = cmpLv8.getComponents()
																qdx = 0
																for cmpLv9 in cmpList9:
																	qdx += 1
																	lv9CmpCnt += 1
																	cListAll = lvlTasks(lvl,odx,cmpLv9,len(cmpList9),cListAll)
																	
																lvl -= 1

														lvl -= 1

												lvl -= 1


										lvl -= 1
#									except:
#										pass
									
								lvl -= 1
#							except:
#								pass
						lvl -= 1
#					except:
#						pass
				lvl -= 1
#			except:
#				pass
		lvl -= 1
#	except:
#		pass
lvl -= 1
print 'Components at Level 1: %s\r\nComponents at Level 2: %s\r\nComponents at Level 3: %s\r\nComponents at Level 4: %s' \
		'\r\nComponents at Level 5: %s: \r\nComponents at Level 6: %s\r\nComponents at Level 7: %s: \r\nComponents at Level 8: %s' \
		'\r\nComponents at Level 9: %s: \r\n' % (lv1CmpCnt,lv2CmpCnt,lv3CmpCnt,lv4CmpCnt,lv5CmpCnt,lv6CmpCnt,lv7CmpCnt,lv8CmpCnt,lv9CmpCnt)
for c in cListAll:
	print c

Oy!

Recursion would be SO much easier to read.

Start with something like this:

#------------
# Recursively scan a container for children and the children's children, calling
# a given routine for each child encountered.
#
def forEachChild(container, func, lvl=None):
	if not lvl:
		lvl = 1
	func(container, lvl)
	for i in range(container.getComponentCount()):
		component = container.getComponent(i)
		forEachChild(component, func, lvl+1)

Pass it a function that takes a component and the depth as its parameters.

{ Note that this version enumerates the nested objects of regular components, too. }

4 Likes

Thanks for the advice Phil. I’ll give it a try.

However, since I haven’t figured out what the “unusual thing” is (point number 4 above) I’m wondering if recursion will exacerbate it due to increased memory usage. On the other hand, cleaning up my code with recursion might fix a bug I haven’t been able to find.

The issue you’re seeing in point #4 may be due to the script trying to set the visible property on ALL components - including the “always hidden” base level components that are parents of the Root Container. If you view your current printout to determine the object types that are parents of the Root Container, you can add a check in your script to make sure you aren’t trying to set the visible property on these objects. (See edit below) I’m not certain, but I think that will fix your print once behavior.

Edit: Actually, the easier fix is probably to just directly point your script at the Root Container component as your starting point. You really don’t want to be touching the components above the Root Container, and including them in your script just adds more noise. You could replace:

system.gui.getParentWindow(event)

with

system.gui.getParentWindow(event).getComponentForPath('Root Container')

And you wouldn’t have to worry about checking component types.

2 Likes

Thanks for the help Will. Between using Phil’s recursion and skipping everything ABOVE the root container, everything works beautifully. And the code went from around 140 lines to 18. It’s hard for me to wrap my head around recursion, but if there was ever a use case for it, this is it.

2 Likes