# OPC Browse Tree

**URL:** <https://forum.inductiveautomation.com/t/opc-browse-tree/12291>\
**Category:** Ignition\
**Created:** [October 26, 2016, 7:40pm UTC](https://forum.inductiveautomation.com/t/opc-browse-tree/12291 "2016-10-26T19:40:18Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![scicco](https://avatars.discourse-cdn.com/v4/letter/s/e0b2c6/32.png) [@scicco](https://forum.inductiveautomation.com/u/scicco)\
**Post date:** [October 26, 2016, 7:40pm UTC](https://forum.inductiveautomation.com/t/opc-browse-tree/12291/1 "2016-10-26T19:40:18Z")

</div>

Hi everyone,

Besides dragging items from the OPC Browser into the Tag Browser and then using the Tag Browse Tree component is there any other way to give a client the ability to browse OPC tags? I’m thinking it’s possible with some logic, a bunch of system.opc.browse commands, and a tree view component but didn’t want to write all that if there was something obvious I missed.

Thanks,  
Steve

---

<div class="post-metadata">

**Author:** ![scicco](https://avatars.discourse-cdn.com/v4/letter/s/e0b2c6/32.png) [@scicco](https://forum.inductiveautomation.com/u/scicco)\
**Post date:** [October 28, 2016, 8:36pm UTC](https://forum.inductiveautomation.com/t/opc-browse-tree/12291/2 "2016-10-28T20:36:41Z")

</div>

I decided to write it and it wasn’t that difficult. In case it helps anyone in the future here’s the code. All you need is a window with a Tree View component. There’s an internalFrameActivated event on the window to populate the root with all your devices:

[code]# Get the current dataset from the tree view object  
tvItems = system.gui.getParentWindow(event).getComponentForPath(‘Root Container.Tree View’).data

# Convert it to a PyDataSet so we can iterate through it

pyDS = system.dataset.toPyDataSet(tvItems)

# Delete the existing rows

for node in pyDS:  
tvItems = system.dataset.deleteRow(tvItems, 0)

# Get top-level opc tag data by device (the propertyChange event of the Tree View will have the logic needed to drill deeper)

opcServer = “Ignition OPC-UA Server”  
nodeId = “Devices”  
devices = system.opc.browseServer(opcServer, nodeId)

for device in devices:  
device\_name = device.getDisplayName()  
device\_type = device.getElementType()  
device\_nodeId = device.getServerNodeId()

```
device_items = system.opc.browseServer(opcServer, str(device_nodeId).replace('[' + opcServer + ']', ''))
	
for device_item in device_items:
	device_item_name = device_item.getDisplayName()
	device_item_type = device_item.getElementType()
	device_item_nodeId = device_item.getServerNodeId()
	
	if str(device_item_type).upper() == "OBJECT":
		# Create as folder with placeholder as item. This allows tree to quickly be built but parent folder must be double-clicked for items to be populated
		icon = "Builtin/icons/16/about.png"		
		sep_char = system.gui.getParentWindow(event).getComponentForPath('Root Container.Tree View').separationCharacter
		tvItems = system.dataset.addRow(tvItems, [device_name + sep_char + device_item_name, "Placeholder", icon, "", "", "(double-click parent folder to refresh)", "", "", icon, "", "", "", "", str(device_item_nodeId).replace('[' + opcServer + ']', ''), device_item_type])
	else:
		icon = "icon-tag.png"
		tvItems = system.dataset.addRow(tvItems, [device_name, device_item_name, icon, "", "", "", "", "", icon, "", "", "", "", str(device_item_nodeId).replace('[' + opcServer + ']', ''), device_item_type])

```

#assign the new dataset to the component data property  
system.gui.getParentWindow(event).getComponentForPath(‘Root Container.Tree View’).data = tvItems[/code]

Then there’s a propertyChange event on the Tree View:

[code]if event.propertyName == “selectedPath”:  
selectedItem = event.newValue

```
# Get the current dataset from the tree view object
tvItems = event.source.parent.getComponent('Tree View').data

# Convert it to a PyDataSet so we can iterate through it
pyDS = system.dataset.toPyDataSet(tvItems)

i = 0		
# Find selected item in Tree View's dataset
for row in pyDS:
	# Get data for selected item 
	# - nodeId and elementType are custom columns added to the end of the Tree View's dataset																														
	path = row["path"] 
	text = row["text"]		
	nodeId = row["nodeID"]
	elementType = row["elementType"]
	if path == selectedItem and text == "Placeholder":			
		if elementType.upper() == "OBJECT":
			# It's a folder that was clicked on so drill into its items											
			opcServer = "Ignition OPC-UA Server"								
			device_items = system.opc.browseServer(opcServer, nodeId)
				
			# Following loop will add items to Tree View dataset and then delete placeholder record					
			for device_item in device_items:
				device_item_name = device_item.getDisplayName()
				device_item_type = device_item.getElementType()
				device_item_nodeId = device_item.getServerNodeId()
				
				if str(device_item_type) == "OBJECT":
					icon = "Builtin/icons/16/about.png"
					sep_char = system.gui.getParentWindow(event).getComponentForPath('Root Container.Tree View').separationCharacter
					tvItems = system.dataset.addRow(tvItems, [path + sep_char + device_item_name, "Placeholder", icon, "", "", "", "", "", icon, "", "", "", "", str(device_item_nodeId).replace('[' + opcServer + ']', ''), device_item_type])
				else:
					icon = "icon-tag.png"
					tvItems = system.dataset.addRow(tvItems, [path, device_item_name, icon, "", "", "", "", "", icon, "", "", "", "", str(device_item_nodeId).replace('[' + opcServer + ']', ''), device_item_type])												
			
			tvItems = system.dataset.deleteRow(tvItems, i)	
						
			# Refresh Tree View with updated dataset
			event.source.parent.getComponent('Tree View').data = tvItems
			
			# For testing purposes, refresh table showing Tree View dataset
			table = event.source.parent.getComponent('Power Table')
			system.db.refresh(table, "data")				
		break
	else:
		i += 1[/code]

```

This only browses one level at a time so it doesn’t take long. The only thing I’m not pleased with is there’s no event on the Tree View I could find for expanding and collapsing nodes so you have to double-click to trigger the selectedPath propertyChange event to populate each level.
