Project Properties in python?

Is there a way to access the project properties in python to get things such as the default database name or the polling base rate, even if it’s read-only?

Yea, sure. All you need is the client or designer context.

However, the way that you get the context in Python scripting is a bit hacky and unsupported. That’s why I just made a feature request to provide supported functions for getting the client/designer/gateway context: inductiveautomation.com/forum/vi … 71&t=11563

Here’s how you can get a client or designer context so you can get the default database name and polling rate etc. in Python. (yay Python!!!).

Throw this in an event handler somewhere:

def getContext(event):
   import system
   comp = event.source
   if system.util.getSystemFlags() & system.util.DESIGNER_FLAG:
      from com.inductiveautomation.ignition.designer import IgnitionDesigner
      while not isinstance(comp,IgnitionDesigner):      
         comp = comp.parent
      context = comp.getContext()
   else:
      from com.inductiveautomation.factorypmi.application.runtime import ClientPanel
      while not isinstance(comp,ClientPanel):
         comp = comp.parent
      context = comp.getClientContext()
   return context

context = getContext(event)

defaultDatasourceName = context.getDefaultDatasourceName()
pollRate = context.getTagPollRate()

print defaultDatasourceName, pollRate

By the way I look forward to meeting you at the next Ignition conference.

Nick

Thanks, Nick. It was one of your most recent posts which inspired my question. It’ll mark it as good when I get a chance to test it.

We had a need for context access from the script console and other spots where we couldnt use an event object. This is what we use…

from system.util import CLIENT_FLAG, DESIGNER_FLAG
from javax.swing import JFrame
from java.awt.event import WindowEvent

try:
	from com.inductiveautomation.ignition.designer import IgnitionDesigner
except ImportError:
	pass

from com.inductiveautomation.factorypmi.application.runtime import ClientPanel


class ActiveContext(object):
	"""
	Singleton for a stable ignition context reference. 
	NOTE: Mainly for code running where there is no event 
	object to derive context access. i.e. 
	scripts launched automatically or script-console..
	# Has been tested for Designer, 
	# Script Console, Client [Full Screen, Published].
	# We call this as a startup script.
	# Tested on Ignition Version 7.9.3
	# Certian API dependencies may need to be resolved 
	# on versions before or after (7.9.3).
	"""

	def __new__(cls):
		"""
		Hook into the class constructor.
		Adds an class attribute reference to the
		instance.
		"""
		if not hasattr(cls, 'instance'):
			cls.instance = super(ActiveContext, cls).__new__(cls)
		return cls.instance
   
	def __init__(self):
		"""Get context."""
		self.context = self.resolveContext()
		self.valid = True
	
	def isValid(self):
		"""Is Context Valid."""
		return self.valid
	
	def setValid(self):
		"""Set Valid State."""
		self.valid = False if self.valid else True
	
	def resolveDesignerContext(self):
		"""Return designer context."""
		try:
			return IgnitionDesigner.getFrame().getContext()
		except AttributeError:
			pass
		self.isValid()
		
	def resolveClientContext(self):
		"""
		Attempt to resolve client context.
		By creating a frame it prevents the need to
		traverse the object heirarchy. i.e. parent.parent....
		"""
		# acquire project name.
		projName = system.util.getProjectName()
		# Frame title.
		frame_name = 'ThrowAway'
		# Create disposable top level frame object.
		frm = JFrame(frame_name)
		# Adjust close behavior.
		frm.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE)
		# get the windows.
		windows = frm.getWindows()
		# cleanup frame.
		frm.dispose()
		# iterate windows, filter on the title containing the 
		# project name.
		for window in windows:
			try:
				if projName in window.getTitle():
					pane =  window.getContentPane()
					# Compare the content pane instance 
					# to the ClientPanel object.
					if isinstance(pane, ClientPanel):
						if hasattr(pane, 'getClientContext'):
							return pane.getClientContext()
			except AttributeError:
				pass
		self.isValid()	
		
	def resolveContext(self):
		"""Attempt to resolve the Context."""
		current_flag = system.util.getSystemFlags()
		ctx = self.resolveDesignerContext
		# If False assumes the client context.
		if current_flag != DESIGNER_FLAG:
			ctx = self.resolveClientContext
		return ctx()		

	def __call__(self):
		"""Calls to the Instance return the Context."""
		return self.getContext()
	
	def getContext(self):
		"""Return Context."""
		return self.context


def initActiveContext():
	"""Called from startup script"""
	ActiveContext()
	

def acquireContext():
	"""Returns context reference."""
	active_context = ActiveContext()
	if active_context.isValid():
		return ActiveContext().getContext()



1 Like

That’s very interesting, thank you. I was wondering, do you know of a way to get the GatewayContext from a gateway script? Like gateway timer script, startup script, tag change etc.

Unsupported.....

from com.inductiveautomation.ignition.gateway import SRContext
ctx = SRContext.get()
1 Like

And very dangerous - if you go down this road, take frequent gateway backups, because it's very possible to permanently break things.

3 Likes

Yeah, I should have mentioned I was aware of SRContext from the forums here but noticed its nowhere to be seen in the documentation, and I have heard about problems with it before. Given the impact something like that could have on our gateways, I’m staying far, far, far away from that.

1 Like

The safer way to get access to the gateway context to perform high-level operations is to write your own module - if you’re already comfortable doing this high-level scripting, it’s not that much of a leap to module development.

1 Like

I thought that’d be the best way - I’m thinking about making that leap some time, but the right opportunity to pour the time into that hasn’t been there just yet. As for this particular issue, its more of a “would be cool if I could do this”, not a “pour many hours into making a new module” type thing.

1 Like

Debatable. {my bold} If you are using the documented GatewayContext methods, the returned objects are identical via module context and SRContext. I would expect the danger to be in the extra functionality in the SRContext, but you must use reflection to explore that. If there's danger in the documented GatewayContext (and I would suggest there is), writing a module instead of using SRContext won't mitigate it. And it is really easy to crash a gateway with a sloppily-coded module.

I said "debatable" because learning the module life cycle in enough detail to avoid such crashes is good preparation for intelligent access to such resources via scripting. Anyways, support for anything done with Module SDK documentation has always been explicitly disavowed by IA.

3 Likes