Why getChild() Fails Across Views
The chain self.page.props.primaryView.getChild("root").getChild("Map") throws 'unicode' object has no attribute 'getChild' because primaryView is a string. It holds the path of the view loaded as the page's primary view, not a live view object. Jython 2.7 reports that string type as unicode, and a string has no component tree to walk.
The limit is architectural. Every Perspective view runs isolated from every other view, and a popup is a separate view even when it sits on top of the view you want to control. Component navigation (self.parent, getSibling(), getChild(), self.view.rootContainer) resolves only inside the view that owns the running script. Vision let one window reach into another window's root container through the system.nav and system.gui packages. Perspective has no equivalent, and no path string or traversal trick returns a component that lives on another view.
Two channels move data between Perspective views: session custom properties, and messages consumed by message handlers. A component method such as the Map's flyTo() has to be called by a script running on the Map's own view, so the design question is which channel triggers that script.
Cross-View Options Compared
| Approach | Reaches the Map view | Can trigger flyTo()
|
Pages affected | Behavior to plan for |
|---|---|---|---|---|
| Direct traversal from the popup | No | No | n/a | Fails with the 'unicode' attribute error |
| Session custom property written by the popup | Yes, as a value | Indirectly, through a change script on the Map side | Every page in the session | Value persists after the popup closes; repeating an identical request writes the same value and does not register as a change |
system.perspective.sendMessage() with scope="session"
|
Yes | Yes, from the handler | Every page in the session | Two browser tabs in one session both execute flyTo()
|
system.perspective.sendMessage() with scope="page"
|
Yes; popup and primary view share the page | Yes, from the handler | Only the page the user is working in | Default scope when none is passed; handler must listen at Page |
system.perspective.sendMessage() with scope="view"
|
No; stays inside the sending view | No | The popup only | Useful inside the popup, not across views |
Session properties fit shared state that several views read (a selected asset, a unit preference). They are a poor fit for a one-shot command like "fly to this location" because the command becomes sticky state and duplicate commands collapse into no change.
Recommended Design: Page-Scoped Message Handler on the Map
Put a message handler directly on the Map component and send to it from the popup at page scope. A popup opens on a page, and page scope covers the primary view and any popups on that page, so the message lands on the Map the user is looking at and nowhere else.
Session scope also works, and it is the scope many first working implementations end up on. The cost is reach: if the same user has two tabs open under one session, a session-scoped message makes both Maps fly. Page scope removes that case at no extra effort, so design for it from the start.
Two rules decide whether the message arrives:
-
Scope must match on both ends.
sendMessage()defaults to page scope whenscopeis omitted. A handler configured to listen at Session only ignores a page-scoped message, which looks like the handler never fires. State the scope explicitly in every call rather than relying on the default. - The receiving view must be open. Handlers only consume messages while their view is loaded. If the Map view is not on the page when the popup sends, nothing receives the message.
Settings Reference
| Item | Where | Setting | What confirms it |
|---|---|---|---|
| Message handler | Map component, Configure Scripts, Message Handlers | Name identical to the messageType string sent by the popup (case-sensitive) |
Handler name visible under the Map in the script configuration |
| Listen scope | Same handler | Page enabled | Page checkbox ticked; Session left off unless multi-tab reach is wanted |
| Send call | Popup event script (button action, change script) |
scope="page" passed explicitly |
Scope visible in the script text |
| Payload | Popup event script | A dictionary carrying everything flyTo() needs |
Logged payload on the receiving side shows every expected key |
Commissioning Procedure
Before anything else, confirm the Map lives on the view opened as the page's primary view (or on another view that is loaded on the same page when the popup is used). Do not build the popup side until the handler exists and fires on its own.
-
Look up the
flyTo()argument structure. Open the Map component's method reference in the Ignition User Manual for your installed version and note the arguments it expects. The payload you design in step 4 has to carry exactly that data. Confirmation: you can write theflyTo()call by hand for one fixed location. -
Create the handler on the Map component. Select the Map, open Configure Scripts, add a message handler, and give it a name such as
mapFlyTo(example name; any string works as long as sender and receiver match). Enable the Page listen scope. Confirmation: the handler appears under the Map with Page ticked. -
Write the handler script. The handler runs in the Map component's context, so
selfis the Map andself.flyTo()is reachable.
Confirmation: the script saves without syntax errors.def onMessageReceived(self, payload): # payload keys are defined by you in the popup script target = payload.get("target") if target is None: system.perspective.print("mapFlyTo: no target in payload") return system.perspective.print("mapFlyTo received: " + str(payload)) # Pass target in the form documented for flyTo() in your Ignition version self.flyTo(target) -
Send from the popup. In the popup's button
onActionPerformedevent (or whichever event commits the user's choice), build the payload and send it with the scope stated.
Confirmation: the message type string matches the handler name character for character.target = ... # build from popup inputs, in the structure flyTo() expects payload = {"target": target} system.perspective.sendMessage("mapFlyTo", payload, scope="page") -
Close the popup after sending, if required. Call
sendMessage()first, then close the popup in the same script. The Map's view stays open, so its handler still consumes the message. -
Remove the diagnostic print once verified. Leave the
Noneguard in place so a malformed payload returns quietly instead of raising inside the handler.
Symptoms and Causes
| Symptom | Cause | Correction |
|---|---|---|
'unicode' object has no attribute 'getChild' |
self.page.props.primaryView is a view path string, not a view object |
Replace traversal with a message handler on the Map |
| Popup script runs, Map does nothing, no error | Handler listens at Session only; sendMessage() called without scope defaults to page |
Pass scope="page" and enable Page on the handler (or match both at Session deliberately) |
| Map does nothing, scopes match | Message type and handler name differ (spelling, case) | Copy the handler name into the sendMessage() call |
| Map does nothing when the popup is opened from a different primary view | The view holding the Map is not open on that page, so no handler exists to consume the message | Open the Map view before sending, or store the request in a session custom property the Map view reads when it loads |
| Map in a second browser tab also moves | Message sent at session scope | Send at page scope and turn off Session on the handler |
Handler fires, flyTo() raises an error |
Payload structure does not match the method's documented arguments | Log the payload in the handler and compare against the manual's method reference |
| Identical second request ignored (session property design) | Writing the same value does not register as a change | Switch to a message handler, which fires on every send |
Scope Decisions for Other Cross-View Commands
The same pattern applies to any method call or property write that must happen on a view other than the one where the user acts: resetting a table filter from a docked navigation panel, refreshing a chart from a popup, clearing a form from a header view. Choose scope by the set of pages that should react:
- Page: the command belongs to what the user is looking at now. This covers most popup-to-view actions.
- Session: the command must reach every tab the user has open, such as a logout cleanup or a session-wide preference change.
- View: the sender and receiver sit inside the same view; no cross-view reach.
A handler can listen at more than one scope. Enable only the scopes you actually send on, so a stray session-scoped message from another part of the project cannot trigger the Map.
Verification
- Open the page in a browser session, launch the popup, and trigger the send. Confirm the
mapFlyTo received:line appears in the browser console output fromsystem.perspective.print(), showing the full payload. - Confirm the Map moves to the requested location. If the log line appears but the Map does not move, the fault is in the
flyTo()arguments, not the messaging. - Send the same location twice in a row and confirm the handler logs twice. A message handler fires on every send regardless of value.
- Close the popup immediately after sending and confirm the Map still moves.
- Open a second browser tab in the same session showing the same Map view. Trigger the popup in the first tab and confirm the Map in the second tab stays where it is. Movement in the second tab means the send or the handler is still at session scope.
FAQ
What happens if I call system.perspective.sendMessage without a scope?
The call defaults to page scope. A handler that listens only at Session will not receive it, so the Map silently does nothing; pass scope="page" explicitly and enable Page on the handler.
What happens if the view with the Map is not open when the popup sends the message?
No handler exists to consume it, so the message has no effect. Open the Map view first, or write the request to a session custom property that the Map view reads when it loads.
What happens if I use session scope and the user has two tabs open?
Both pages in that session receive the message and both Maps execute flyTo(). Page scope limits the action to the page where the popup was used.
What happens if I close the popup right after calling sendMessage?
The Map still receives the message, because the handler lives on the Map's view, which stays open. Send first, then close the popup in the same script.
Can a Perspective popup use getChild to reach a component on the main view?
No. Each view runs isolated, and self.page.props.primaryView is only the view path string, which is why getChild raises a 'unicode' attribute error; use a message handler or a session custom property instead.