Fetching a component's data from window and transfer that value to Translation Manager

Hi everyone, I have facing some error while getting a string values of components from a window. Me and my teamates ah standardize our Ignition system like, Scripting method, Given names of Components. So, we need to develop one function for fetching a String values of used components on window like, We had created "Window_1" named window and we had put a Label, Numeric Text Field, Text Field, Dropdown components are presented on that window. And we have passed some string values to components. So, we need to fetching the string value from that component and translate that English to Other languages like italic, French, and other Languages using a translation Manager. If we need to fetching a data of that component, we need to read the component's Syntex. Like Label Have a "Text" Syntex, Text field have a "Text" and Dropdown have a "Data" Syntex. In here, we have try to read the Syntex of the label, text Field, that had been done. And, we try to read the data Syntex of the Dropdown component, But cannot able to do that.

def getComponentsOnWindow(displayPath):
    # Empty component list
    
	componentList = []
	window = system.gui.getWindow(displayPath)
	container = window.getRootContainer()

	for component in container.components:
		try:
		    window = system.gui.getWindow(displayPath)
		    text = window.rootContainer.getComponent(str(component.name)).text
		    print text
		except:
#			window = system.gui.getWindow(displayPath)
		    text = window.rootContainer.getComponent(str(component.name)).data

name = "Test_Folter/Window_3" #window Path
getComponentsOnWindow(name)

image

This is a my window, i need to read the all components of this window.

This one is i have used.i put that script to one button on Mouse Clicked event.

This script has written by @Paul.Scott in previous topic of Iterating through components on a screen through scripting

The way I do this is any component who has a value I care to scrape off a window, I give it a custom property called outValue, bind that to data I want to get off the component, and then when I iterate components I try/except getting that value. That way you don't have to try/except getting text or getting data because what happens the moment you now need an integer or float value?

I put all these values into a dictionary of {'componentName':outValue, ...}. I also then name my components to match my db columns - then I can feed this dictionary directly to named queries.

Here' sthe script I use - note that you can also add root container properties - if they have a _out_ suffix in their name, they will also be added to the dictionary

"""
Grabs all relevant data off of a window for database insert/updates.
"""
import com.inductiveautomation.factorypmi.application.components.BasicContainer

def parseContainer(rc, recursive=False, dataDictionary=None):
	"""
	Creates a dictionary that is to be used in conjunction of named queries.
	Key values are the database column names, and the values are the database column values.
	Looks to root container and grabs any custom properties that have a "_out_" prefix, strips that off an uses remained as the key name.
	Iterates through all components and checks if there is an outValue.  If so, the name of the component will be used as the key value,
	and value of outValue as the value.
	Args:
		rc: root container of the window.
		recursive: bool, if True, runs this same thing on any child containers down
		dataDictionary: dictionary, key/values are added to this used in the case of recursive (though guess a coder could prepopultae a dictionary with other stuff
		and feed it in if they so chose to but no real reason to)
	Returns:
		dictionary {"column_name_1":column_value_1, "columne_name_2":column_value_2, ...}
	"""
	if dataDictionary is None:
		dataDictionary = {}
	# Gets properties off of root container and parses name nicely
	for prop in rc.properties:
		propName = str(prop.getName())
		if propName.startswith("_out_"):
			keyName = propName.replace("_out_", "")
			dataDictionary[keyName] = prop.getValue()
	# Iterates through a flat window.  Will need to modify this to search deeper into containers
	for component in rc.components:
		# Recursive step for inner containers
		if isinstance(component, com.inductiveautomation.factorypmi.application.components.BasicContainer) and recursive:
			parseContainer(component, dataDictionary=dataDictionary)
		for prop in component.getProperties():
			if str(prop.getName()) == 'outValue':
				dataDictionary[component.getName()] = prop.getValue()		
	return dataDictionary

def getComponent(rc, nameOfComponent):
	"""
	Grabs a compoennt reference off of a given root container.  Recursively searches for name.  Returns first instance.
	"""
	if nameOfComponent=='rc':
		return rc
	else:
		for component in rc.components:
			if isinstance(component, com.inductiveautomation.factorypmi.application.components.BasicContainer):
				getComponent(component, nameOfComponent)
			if component.name == nameOfComponent:
				return component
		return None

Edit: Just noticing a few unused lines / unnecessary print statements you may want to remove.

I agree with your general idea of making a standardized way of scraping a window and this is my method after a few iterations of trying to do that.

Having said all that, I don't know that this is the right way to handle transalations.

2 Likes

Here is a script that will print all of the text or string data properties from your window components: (Including the dropdown data)

from com.inductiveautomation.factorypmi.application.components import BasicContainer
from java.lang import String
def printDatasetStrings(dataset):
	if dataset.rowCount > 0 and dataset.columnCount >0:
		for column in range(dataset.columnCount):
			value = dataset.getValueAt(0, column)
			if isinstance(value,(String, unicode)):
				print dataset.getColumnAsList(column)
def printTextAndDataStrings(container):
	for component in container.getComponents():
		if isinstance(component, BasicContainer):
			printTextAndDataStrings(component)
		if hasattr(component, 'text'):
			print component.text
		if hasattr(component, 'Text'):
			print component.Text
		if hasattr(component, 'data'):
			printDatasetStrings(component.data)
		if hasattr(component, 'Data'):
			printDatasetStrings(component.Data)
rootContainer = system.gui.getParentWindow(event).rootContainer
printTextAndDataStrings(rootContainer)
2 Likes

Thank you @bkarabinchak.psi for your valurable suggestion. here i fetching the components values from vision not a Perspective. But, your script is usefull one. When we have to standartize the perspective, that time we use this one. if you have a any suggesion for me., You can reply for this chat..,

Thank you @justinedwards.jle for your suggesion. It's running good, that was i needed.

1 Like

My script is for vision. Root containers are a vision thing. Perspective uses a variety of view containers.

this might be intersting

Hi @justinedwards.jle thank you for this suggession, but i need a one ore clarification. shal we take a values of components on the Template and template repeater in vision. is it possible or not ?

thanks,

It's not difficult. For a template repeater, use the getLoadedTemplates method, and for templates in a container, look for the template holder class.

yeah @justinedwards.jle , i tried that onein my script. i using the templateHolder and VisionTemplate class in function.
image

Then, i have seen the name of the window by using the print function. it shows the name the template, but cannot able to read the String values of the component on the template.

the template windows are,

I gives name of the window is Window_Template. And i put the template in another window.That is,
image
here i given the name of the template is "Window_template". Then, when i run the above script, that time it'll shows the Template name, but didn't given the values.

from com.inductiveautomation.factorypmi.application.components.template import VisionTemplate
from com.inductiveautomation.factorypmi.application.components.template import TemplateHolder
from com.inductiveautomation.factorypmi.application.components import BasicContainer
from java.lang import String

def printTextAndDataStrings(container):

	ins_list = []

	for component in container.getComponents():
	
		if isinstance(component, BasicContainer):
			printTextAndDataStrings(component)
			
		if isinstance(component, TemplateHolder):
			print "Yes, this one is a TemplateHolder"
			print component.name
			printTextAndDataStrings(component)
			
		if isinstance(component, VisionTemplate):
			print "Yes, this one is a Template"
			print component.name
			printTextAndDataStrings(component)
			
		if hasattr(component, 'text'):
			val = component.text
			ins_list.append(str(val))
			
		if hasattr(component, 'Text'):
			val = component.Text
			ins_list.append(str(val))
			
		if hasattr(component, 'data'):
			dataset = component.data
			
			if dataset.rowCount > 0 and dataset.columnCount >0:
				
				for column in range(dataset.columnCount):
					value = dataset.getValueAt(0, column)
					
					if isinstance(value,(String, unicode)):
						val = dataset.getColumnAsList(column)
				
				for x in val:
					ins_list.append(str(x))
			
		if hasattr(component, 'Data'):
			dataset = component.data
			
			if dataset.rowCount > 0 and dataset.columnCount >0:
				
				for column in range(dataset.columnCount):
					value = dataset.getValueAt(0, column)
					
					if isinstance(value,(String, unicode)):
						val = dataset.getColumnAsList(column)
				
				for x in val:
					ins_list.append(str(x))
			
	query = "SELECT Keyes FROM translation"
	val = system.db.runQuery(query,"MySQL")
	val1 = val.getColumnAsList(0)
	
	list = []
	for x in ins_list:
	
		if x in val1:
			pass
			
		else:
			list.append(str(x))
			
	y = "('" + "'),('".join(list)+ "')"
#	print y
	
	if y != "('')":
		query = "insert into translation (Keyes) values {}".format(y)
		run = system.db.runPrepUpdate(query,[],"MySQL")
		print run

windows = system.gui.getWindowNames()
e_list = []

for path in windows:
    e_list.append(path)

for x in e_list:
#	system.nav.openWindow(x)
	rootContainer = system.gui.getWindow(str(x)).rootContainer
	printTextAndDataStrings(rootContainer)
	

This one is i using for fetching the al string values from windows and Templates.

Thanks,

I don't imagine that you need the if isinstance(component, VisionTemplate): logic.

What you need is this:

#Don't send the templateHolder, send the template loaded in the holder
if isinstance(component, TemplateHolder):
	printTextAndDataStrings(component.loadedTemplate)

For a template repeater, it's the same thing, except there are more than one, so you will need to iterate the list:

from com.inductiveautomation.factorypmi.application.components import TemplateRepeater
if isinstance(component, TemplateRepeater):
	for template in component.loadedTemplates:
		printTextAndDataStrings(template)

It's run well. Thank you @justinedwards.jle for your support..,

Thanks,

1 Like

Can we get a component in a window on ignition without opening the window? is it possible or not? Because, whenever I try to run that script in vision on the client that makes the gateway has more Hang or lag. I had one test project containing 70 to 80 windows Mainwindow, popup windows, and stocked windows. Then I run that script on the client, I open and close each and every window in the client in the background. for the 1 to 40 window it did not through any error or not hank and for the 41 to 70 or 80 window the client has been hanged. This is my biggest problem in this project. i think if we reduce the open and close time in the client or designer, we'll avoid the thanking of the system. That's why I asking if is there any solution without opening the window to get the component of the window.

Thanks,

did you take alook at this?

this checks the files without opening windows to find all "texts" (doesnt work with bindings though)

its still a slow process too, but probaly way faster than what you are doing

hi @victordcq, Shall we use this in Vision client? Like putting a script in a button and executing that in the client.

from com.inductiveautomation.factorypmi.application.binding.action import ActionAdapter

class ProjectResources:
	
	def __init__(self):

		import system
		from javax.swing import JFrame
		from java.awt.event import WindowEvent
		
		if system.util.getSystemFlags() & system.util.DESIGNER_FLAG:
			try:
				from com.inductiveautomation.ignition.designer import IgnitionDesigner
			except ImportError:
				pass
			try:
				self.context = IgnitionDesigner.getFrame().getContext()
			except AttributeError:
				pass
		else:
			from com.inductiveautomation.factorypmi.application.runtime import ClientPanel
			"""
			Attempt to resolve client context. By creating a frame it prevents the need to traverse the object heirarchy. i.e. parent.parent....
			"""
			projName = system.util.getProjectName()
			frame_name = 'ThrowAway'
			frm = JFrame(frame_name)
			frm.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE)
			windows = frm.getWindows()
			frm.dispose()
			for window in windows:
				try:
					if projName in window.getTitle():
						pane =  window.getContentPane()
						if isinstance(pane, ClientPanel):
							if hasattr(pane, 'getClientContext'):
								self.context = pane.getClientContext()
								break
				except AttributeError:
					pass
		if not hasattr(self.context, 'getProject'):
			raise Exception(self.getInvalidContextError(context))	

	def getAllProjectResources(self):
		return self.context.getProject().getResources()
	
	def getProjectResource(self, resourcepaths):
		list = []
		for resource in self.getAllProjectResources():
			if str(resource.getResourcePath().getFolderPath()) in resourcepaths :
				list.append(resource)
		return list	 	

	def getResourceInfo(self, resourcepath):
		for resource in self.getAllProjectResources():
			if str(resource.getResourcePath().getFolderPath()) == resourcepath :
				dresource = self.getDeSerializedResource(resource)
				if dresource != None:
					return {'Name' : dresource.getName(), 'Title' : dresource.getTitle() }
				else:
					return {}					
		return {}	 	

	def getAllComponentsForResource(self,resourcepaths):
		rows = []
		header = ['Project name', 'Resource Name','Resource Type', 'Component Type', 'Attribute', 'Name' ,'Value']
		resources = self.getProjectResource(resourcepaths)
		checkedResource=[]
		
		def resolveData(component):
			
			if component.getClass().getSimpleName() == u'TemplateHolder':
				if component.templatePath not in checkedResource:				
					checkedResource.append(component.templatePath)				
					tempResources = self.getProjectResource(component.templatePath)					
					for tempResource in tempResources:											
						dresource = self.getDeSerializedResource(tempResource)
						if dresource != None:
							rows.extend(resolveData(dresource))
							
							tempComponents = self.getAllComponentsForObject(dresource)
							for tempComponent in tempComponents:
								#print tempComponent.getName(), tempComponent.getClass().getSimpleName()								
								rows.extend(resolveData(tempComponent))
								
			#get resources
			arr = []
			if hasattr(component, 'text') and component.name != None and component.text:
				arr.append([resource.getProjectName(), resource.getResourceName(), resource.getResourceType().getTypeId(), component.getClass().getSimpleName(), 'Text', component.name, component.text])	
			if hasattr(component, 'toolTipText') and component.name != None and component.toolTipText:
				arr.append([resource.getProjectName(), resource.getResourceName(), resource.getResourceType().getTypeId(), component.getClass().getSimpleName(), 'ToolTip', component.name, component.toolTipText])	
			if hasattr(component, 'title') and component.name != None and component.title:
				arr.append([resource.getProjectName(), resource.getResourceName(), resource.getResourceType().getTypeId(), component.getClass().getSimpleName(), 'Title', component.name, component.title])	
			return arr			
		
			
		for resource in resources:
			dresource = self.getDeSerializedResource(resource)
			if dresource != None:
				
				rows.extend(resolveData(dresource))
				components = self.getAllComponentsForObject(dresource)
				for component in components:
					#print component.getName(), component.getClass().getSimpleName()					
					rows.extend(resolveData(component))
					
		return system.dataset.toDataSet(header, rows)		
		
	def getDeSerializedResource(self, resource):
		deserializer = self.context.createDeserializer()
		if not resource.isFolder():
			if str(resource.getResourceType().getTypeId()) == 'windows':
				try:				
					return deserializer.deserialize(resource.getData("window.bin")).getRootObjects()[0]
				except:
					pass
			elif str(resource.getResourceType().getTypeId()) == 'templates':
				try:				
					return deserializer.deserialize(resource.getData("template.bin")).getRootObjects()[0]
				except:
					pass
		return None

	def getAllComponentsForObject(self, object):
		arr = []
		try:
			for component in object.getComponents():
				arr.append(component)
				self.objectSearch(component, arr)
		except AttributeError:
			pass		
		return arr
		
	def objectSearch(self, object, array):
		try:
			for component in object.getComponents():
				array.append(component)
				self.objectSearch(component, array)
		except AttributeError:
			return		

allText = ProjectResources().getAllComponentsForResource(system.gui.getWindowNames())

for r in range(allText .getRowCount()):
	key = allText.getValueAt(r, 'Value')
	value = key 
	system.util.modifyTranslation(str(key), str(value), 'en')

yes

tho it can probably be refractered to not need it. by changing the system.gui functions

but im not super familiar with vision and i didnt write this script, so idk if there are other scope conflicts

That's not strictly true. It opens/deserializes them into designer RAM, just not in the designer UI.

But the way to avoid memory problems with a script that opens all windows is to close them within the loop, right after you are done with them. (Just avoid closing the window that is running the script.)

1 Like

anyways for all projects so far we were able to run this without problems. ofc it was always done before production so yeah, it didnt really matter to us if this took a while (we used deeple to autotranlate these aswell).

(
for the leftover texts from bindings or db i had made script for vision (and a component for persperctive) that added an option to the contextmenu on all textfields and combobox to open a popup to import/change the translations of the textfield or all labels of the combobox.
)

I have face some error using the below script..,

from com.inductiveautomation.factorypmi.application.binding.action import ActionAdapter

class ProjectResources:

	def __init__(self):

		import system
		from javax.swing import JFrame
		from java.awt.event import WindowEvent

		if system.util.getSystemFlags() & system.util.DESIGNER_FLAG:
			try:
				from com.inductiveautomation.ignition.designer import IgnitionDesigner
			except ImportError:
				pass
			try:
				self.context = IgnitionDesigner.getFrame().getContext()
			except AttributeError:
				pass
		else:
			from com.inductiveautomation.factorypmi.application.runtime import ClientPanel
			"""
			Attempt to resolve client context. By creating a frame it prevents the need to traverse the object heirarchy. i.e. parent.parent....
			"""
			projName = system.util.getProjectName()
			frame_name = 'ThrowAway'
			frm = JFrame(frame_name)
			frm.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE)
			windows = frm.getWindows()
			frm.dispose()
			for window in windows:
				try:
					if projName in window.getTitle():
						pane =  window.getContentPane()
						if isinstance(pane, ClientPanel):
							if hasattr(pane, 'getClientContext'):
								self.context = pane.getClientContext()
								break
				except AttributeError:
					pass
#		print context
		if not hasattr(self.context, 'getProject'):
			raise Exception(self.getInvalidContextError(context))

	def getAllProjectResources(self):
		return self.context.getProject().getResources()
	
	def getProjectResource(self, resourcepaths):
		list = []
		for resource in self.getAllProjectResources():
			if str(resource.getResourcePath().getFolderPath()) in resourcepaths :
				list.append(resource)
		return list	 	

	def getResourceInfo(self, resourcepath):
		for resource in self.getAllProjectResources():
			if str(resource.getResourcePath().getFolderPath()) == resourcepath :
				dresource = self.getDeSerializedResource(resource)
				if dresource != None:
					return {'Name' : dresource.getName(), 'Title' : dresource.getTitle() }
				else:
					return {}
		return {}

	def getAllComponentsForResource(self,resourcepaths):
		rows = []
		header = ['Project name', 'Resource Name','Resource Type', 'Component Type', 'Attribute', 'Name' ,'Value']
		resources = self.getProjectResource(resourcepaths)
		checkedResource=[]
		
		def resolveData(component):
			
			if component.getClass().getSimpleName() == u'TemplateHolder':
				if component.templatePath not in checkedResource:
					checkedResource.append(component.templatePath)
					tempResources = self.getProjectResource(component.templatePath)
					for tempResource in tempResources:
						dresource = self.getDeSerializedResource(tempResource)
						if dresource != None:
							rows.extend(resolveData(dresource))
							
							tempComponents = self.getAllComponentsForObject(dresource)
							for tempComponent in tempComponents:
								#print tempComponent.getName(), tempComponent.getClass().getSimpleName()
								rows.extend(resolveData(tempComponent))
								
			#get resources
			arr = []
			if hasattr(component, 'text') and component.name != None and component.text:
				arr.append([resource.getProjectName(), resource.getResourceName(), resource.getResourceType().getTypeId(), component.getClass().getSimpleName(), 'Text', component.name, component.text])
			if hasattr(component, 'toolTipText') and component.name != None and component.toolTipText:
				arr.append([resource.getProjectName(), resource.getResourceName(), resource.getResourceType().getTypeId(), component.getClass().getSimpleName(), 'ToolTip', component.name, component.toolTipText])
			if hasattr(component, 'title') and component.name != None and component.title:
				arr.append([resource.getProjectName(), resource.getResourceName(), resource.getResourceType().getTypeId(), component.getClass().getSimpleName(), 'Title', component.name, component.title])
			return arr
		
			
		for resource in resources:
			dresource = self.getDeSerializedResource(resource)
			if dresource != None:
				
				rows.extend(resolveData(dresource))
				components = self.getAllComponentsForObject(dresource)
				for component in components:
					#print component.getName(), component.getClass().getSimpleName()
					rows.extend(resolveData(component))
					
		return system.dataset.toDataSet(header, rows)
		
	def getDeSerializedResource(self, resource):
		deserializer = self.context.createDeserializer()
		if not resource.isFolder():
			if str(resource.getResourceType().getTypeId()) == 'windows':
				try:
					return deserializer.deserialize(resource.getData("window.bin")).getRootObjects()[0]
				except:
					pass
			elif str(resource.getResourceType().getTypeId()) == 'templates':
				try:
					return deserializer.deserialize(resource.getData("template.bin")).getRootObjects()[0]
				except:
					pass
		return None

	def getAllComponentsForObject(self, object):
		arr = []
		try:
			for component in object.getComponents():
				arr.append(component)
				self.objectSearch(component, arr)
		except AttributeError:
			pass
		return arr
		
	def objectSearch(self, object, array):
		try:
			for component in object.getComponents():
				array.append(component)
				self.objectSearch(component, array)
		except AttributeError:
			return

allText = ProjectResources().getAllComponentsForResource(system.gui.getWindowNames())

emp_list = []
for r in range(allText .getRowCount()):
	key = allText.getValueAt(r, 'Value')
	value = key 
	emp_list.append(str(value))
print emp_list
#	system.util.modifyTranslation(str(key), str(value), 'en')This text will be hidden

I try to run that script in the Vision client. But it throws an error I have put that script in Button on the screen then run that one.

Whenever I run that, that will show that attribute error.., like "ProjectResources instance has no attribute 'context'"

Can you pls clarify, what we need to do that to solve this problem?

Thanks..,