After resolving the active designer or client context, Python can read the project's default datasource name and tag polling rate through getDefaultDatasourceName() and getTagPollRate(). The data path is script → execution-scope detection → UI object hierarchy or active window → context object → property getter. The weak point is context acquisition: this technique uses internal Java objects, was tested on Ignition 7.9.3, and was described as unsupported.
Where does the property request start?
Identify the script's execution scope before calling a context method. An event handler begins with event.source and can walk the component hierarchy. The Designer Script Console and automatically launched scripts have no event object, so they require a separately acquired context reference.
| Script location | Starting object | Context path | Primary failure |
|---|---|---|---|
| Designer event handler | event.source |
Ancestor → IgnitionDesigner → getContext()
|
Designer ancestor not found |
| Client event handler | event.source |
Ancestor → ClientPanel → getClientContext()
|
Client panel not found |
| Designer Script Console | Active Designer frame | IgnitionDesigner.getFrame().getContext() |
No active frame or incompatible internal API |
| Client script without an event | Top-level Swing windows | Matching ClientPanel → getClientContext()
|
No uniquely matching client window |
| Gateway scope | Neither of the above | Gateway-specific implementation required | Client and Designer classes are unavailable |
Layer one is object reachability: confirm that the script has either an event source or an active top-level application object. Do not call either property getter until that check identifies a usable path.
How should the script select Designer or Client scope?
system.util.getSystemFlags() supplies execution flags. Test DESIGNER_FLAG as a bit mask rather than comparing the complete flag value for equality. Equality can select the wrong branch when the returned value contains the Designer bit plus another bit.
import system
flags = system.util.getSystemFlags()
inDesigner = bool(flags & system.util.DESIGNER_FLAG)
Import only the class required by the selected branch. A Designer class import does not belong in the client path, and a client runtime class does not establish that the code is in a Designer.
| Test | Meaning | Use |
|---|---|---|
flags & system.util.DESIGNER_FLAG |
Checks whether the Designer bit is present | Preferred branch test |
flags == system.util.DESIGNER_FLAG |
Requires the complete value to equal one flag | Avoid when flags may be combined |
flags != system.util.DESIGNER_FLAG |
Treats every non-exact value as a client | Does not distinguish a gateway or combined flags |
Print or otherwise inspect the Boolean inDesigner in each intended execution location. The check passes only when Designer code selects the Designer branch and a published client selects the client branch.
How does an event handler reach the context?
Walk upward from event.source until the required container type appears. Bound the traversal by checking for None; an unbounded loop otherwise fails indirectly when the parent chain ends.
def getContext(event):
import system
comp = event.source
flags = system.util.getSystemFlags()
if flags & system.util.DESIGNER_FLAG:
from com.inductiveautomation.ignition.designer import IgnitionDesigner
while comp is not None and not isinstance(comp, IgnitionDesigner):
comp = comp.parent
if comp is None:
raise ValueError('IgnitionDesigner ancestor not found')
return comp.getContext()
from com.inductiveautomation.factorypmi.application.runtime import ClientPanel
while comp is not None and not isinstance(comp, ClientPanel):
comp = comp.parent
if comp is None:
raise ValueError('ClientPanel ancestor not found')
return comp.getClientContext()
This path depends on a component event whose source belongs to the application hierarchy. It is not suitable for the Script Console, startup code without an event, gateway scripts, or a detached Swing object.
Test the returned object before reading project settings: context = getContext(event), followed by print context is not None. A printed True proves the hierarchy and scope branch are connected.
How can code resolve a context without an event?
In the Designer, the direct path is the active Designer frame:
from com.inductiveautomation.ignition.designer import IgnitionDesigner
context = IgnitionDesigner.getFrame().getContext()
In a client, the tested workaround creates a disposable JFrame, obtains the top-level window list, disposes the temporary frame, and searches for a window whose title contains system.util.getProjectName(). It accepts the window only when its content pane is a ClientPanel exposing getClientContext().
import system
from javax.swing import JFrame
from com.inductiveautomation.factorypmi.application.runtime import ClientPanel
projectName = system.util.getProjectName()
frame = JFrame('ThrowAway')
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE)
windows = frame.getWindows()
frame.dispose()
context = None
for window in windows:
try:
if projectName in window.getTitle():
pane = window.getContentPane()
if isinstance(pane, ClientPanel) and hasattr(pane, 'getClientContext'):
context = pane.getClientContext()
break
except AttributeError:
pass
if context is None:
raise ValueError('Active client context not found')
The project-name title match is only a candidate filter. The ClientPanel type check is the decisive hop. Breaking after the first valid match prevents a later window from silently replacing the selected context.
Run the resolver in the exact no-event location that needs it. The check passes when context is not None and the temporary ThrowAway frame is no longer displayed.
Which getters return the two project properties?
Once the context exists, the read path is short. Keep acquisition separate from property access so a missing context cannot be mistaken for a missing project value.
defaultDatasourceName = context.getDefaultDatasourceName()
pollRate = context.getTagPollRate()
print defaultDatasourceName, pollRate
| Requested setting | Context method | Verification |
|---|---|---|
| Default database or datasource name | getDefaultDatasourceName() |
Compare the returned name with the project's configured default datasource |
| Polling base rate | getTagPollRate() |
Compare the returned value with the project's configured tag polling rate |
Do not assign units to getTagPollRate() from this code alone. Preserve the raw returned value and compare it with the corresponding project setting for the installed release. These calls read values; the shown procedure does not change project configuration.
The check passes only when both printed values match the same open project's settings. A valid context with unexpected values usually points to selection of the wrong project window or execution scope.
How should a cached context be guarded?
A singleton can make the context available to startup scripts, automatic scripts, and the Script Console. Initialize it once, but treat successful resolution—not object construction—as the validity condition.
The tested ActiveContext pattern contains two traps. Its constructor assigns valid = True after resolution even if resolution returned None. Its failure paths call isValid(), which only reads the state; they do not invalidate it. A method named setValid() also toggles state, making repeated calls alternate between valid and invalid.
class ActiveContext(object):
instance = None
def __new__(cls):
if cls.instance is None:
cls.instance = super(ActiveContext, cls).__new__(cls)
return cls.instance
def __init__(self):
if not hasattr(self, 'context'):
self.context = self.resolveContext()
self.valid = self.context is not None
def isValid(self):
return self.valid and self.context is not None
def getContext(self):
if not self.isValid():
raise ValueError('Ignition context is unavailable')
return self.context
resolveContext() still needs the bit-mask scope selection and the appropriate Designer or client resolver described above. Cache the reference only for the active application lifecycle; clear or reacquire it when that owning context is no longer active.
Call isValid(), then call both getters. The guard passes only when validity is false for a failed resolution and true for a non-null context.
What failures identify the broken hop?
| Symptom | Likely stopping point | Diagnostic action | Correction |
|---|---|---|---|
| Designer import or lookup fails | Wrong execution branch or incompatible internal API | Inspect the Designer flag test and execution location | Use the bit-mask test and import inside the Designer branch |
ClientPanel ancestor not found |
Event source is outside the client component hierarchy | Trace comp.parent until it becomes None
|
Run from a client component event or use the no-event resolver |
Client context remains None
|
No title candidate passed the ClientPanel check |
Inspect window titles, content-pane types, and getProjectName()
|
Select the actual client window; do not accept title matching alone |
| Properties belong to another project | Ambiguous window selection | Compare project name, pane type, and both returned settings | Reject ambiguous candidates rather than using the last match |
| Singleton reports valid with no context | Validity initialized independently of resolution | Print context is not None beside isValid()
|
Derive validity from the resolved object |
Code fails outside 7.9.3
|
Internal class or object-hierarchy dependency changed | Test imports, frame access, pane type, and getters on that installed version | Replace the failing internal hop with a supported version-specific integration |
This client/Designer technique is not a gateway-context transport. Gateway-level operations require a gateway-scope design; a custom module provides that environment but also requires correct module lifecycle handling. Do not import client Swing classes into gateway code.
For the end-to-end check, run the same read in every required scope, record the selected branch and project name, and compare both returned properties with that project's configuration.
FAQ
Can I read the default Ignition database name from Python?
Yes. Resolve the active Designer or client context, then call context.getDefaultDatasourceName(). Compare the result with the current project's configured default datasource.
Does getTagPollRate return the project polling base rate?
The tested context exposes the polling value through getTagPollRate(). Keep the returned value in its native form and verify it against the project's configured tag polling rate.
Can I access Ignition project properties from the Script Console?
Yes, using the Designer context path IgnitionDesigner.getFrame().getContext(). This internal approach was tested on Ignition 7.9.3, so retest every class and method after a version change.
Does the same context code work in Designer and Client?
No single acquisition path covers both. Test system.util.DESIGNER_FLAG; use getContext() from IgnitionDesigner in Designer scope and getClientContext() from ClientPanel in client scope.
Can I verify the context before using its project properties?
Yes. Confirm the context is non-null, print getDefaultDatasourceName() and getTagPollRate(), and compare both results with the same open project's configuration; that is the final end-to-end verification.