Configuring Ignition Row Selector Scripting Properties

David Krause6 min read
HMI / SCADAOther ManufacturerTechnical Reference
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

The row selector component in Ignition 7.6.7 reports ScrollPaneUI as its UI property, so it is a Java Swing JScrollPane subclass. Most of the "undocumented" properties are inherited Swing and AWT bean properties, and Swing documentation applies to them directly. A small set is Ignition-specific: data, dataIn, dataOut, dataQuality, appContext, dynamicProps, filters, allDataNodeText and expandAllDataNode. Setting a selection from script means reaching the component inside the scroll pane's viewport and using its Swing selection API. Listeners are Java interfaces implemented with a Jython class.

Sorting the property list into Swing-inherited and Vision-specific

The listing is a flat dump of every readable bean property, each flagged bindable. Sort it by origin before searching for documentation.

Properties in the listing Origin Where to read about them
UI, UIClassID, columnHeader, columnHeaderView, border, autoscrolls JScrollPane / JComponent Java Swing API docs for javax.swing.JScrollPane
ancestorListeners, componentListeners, containerListeners, focusListeners Registered-listener arrays from JComponent/Component/Container Swing/AWT event model; read-only views of what is attached
focusCycleRoot, focusTraversalKeys, focusTraversalPolicy, focusOwner, focusTraversable AWT focus subsystem AWT focus documentation
background, backgroundSet, cursor, cursorSet, bounds, alignmentX, alignmentY, doubleBuffered, debugGraphicsOptions, dropTarget, componentPopupMenu Component/JComponent Swing API docs
data (RowFilteredDataSet), dataIn/dataOut (Dataset), dataQuality (int), filters (List), appContext (VisionClientContext), dynamicProps (TreeMap), allDataNodeText (String), expandAllDataNode (boolean) Ignition Vision Inspect in the Script Console; these have no Swing equivalent

The listing ends mid-way through the focus properties, so the properties that matter most for selection may sit past the truncation point. Enumerate them live with dir() as shown in the procedure below instead of reading a pasted list.

Why the properties look undocumented

Vision components are Swing objects, and Jython exposes any Java getX()/setX() pair as a property x. Ignition's component introspection lists every such pair, whether or not Inductive Automation wrote a manual entry for it. The result is dozens of entries where only a handful carry Ignition semantics.

Three mechanisms explain most of the confusion:

  • Listener properties are arrays, not hooks. ancestorListeners has type AncestorListener[]. It reports what is already registered. Assigning to it does not register a handler. Registration happens through addXListener() methods, which do not appear as properties.
  • The scroll pane is a container. The visible rows are drawn by a child component held in the scroll pane's viewport. columnHeader (JViewport) and columnHeaderView (Component) confirm a header viewport exists. The selection state lives on the child, not on the JScrollPane.
  • Data is a wrapped dataset. data is a RowFilteredDataSet, so the dataset the user sees can be a filtered subset of dataIn. filters (List) holds the active filter definitions. Row indices in the displayed view and in the underlying dataset can differ when filters are active.

There is no dropdown-style selectedValue in the visible portion of the listing. Confirm absence with hasattr(comp, 'selectedValue') before designing around one.

Discovering the live API of the component in Ignition 7.6.7

  1. Place a button on the same window. In its actionPerformed handler, get the component: comp = event.source.parent.getComponent('RowSelector'). Replace the name with the component's actual name.
  2. Print the wrapper class and data types: print comp.__class__, print comp.data.__class__, print comp.dataIn.rowCount.
  3. Print the child that draws the rows: view = comp.viewport.view, then print view.__class__. Jython maps comp.viewport.view to getViewport().getView().
  4. List every property and method on that child: print dir(view). A setRowSelectionInterval entry indicates a JTable. A setSelectionRow entry indicates a JTree.
  5. Look up the class name printed in step 3 in the Java Swing API docs for the Java runtime bundled with the Ignition client. Read the Java version with from java.lang import System; print System.getProperty('java.version'), because method availability tracks the runtime.
  6. Print comp.filters and compare comp.data.rowCount with comp.dataIn.rowCount. A difference means the view is filtered.

Setting the selection from a script

Branch on the class found in step 4 above.

Child class Select a row Read the selection
JTable view.setRowSelectionInterval(r, r) view.getSelectedRow()
JTree view.setSelectionRow(r) view.getSelectionRows()

These are standard Swing methods; the index r is a view index. When filters is non-empty, translate a dataset row to a view row by searching the displayed data for the key value rather than reusing the dataIn row number. Run the selection call on the Swing event thread. Ignition's system.util.invokeLater serves that purpose when the call originates from a non-UI thread such as a gateway message or timer script.

To mirror a dropdown's selectedValue behavior, keep a custom property on the window, write it from a selection listener, and drive the selection from a change script on that property.

Implementing listeners in Jython

Swing listeners are Java interfaces. Jython implements them with a Python class that extends the interface or its adapter class, then registers an instance through the matching add method.

from java.awt.event import MouseAdapter

class RowClick(MouseAdapter):
    def mouseClicked(self, e):
        print 'clicked at', e.getPoint()

view = comp.viewport.view
view.addMouseListener(RowClick())

For selection changes on a JTable, register on the selection model:

from javax.swing.event import ListSelectionListener

class SelWatch(ListSelectionListener):
    def valueChanged(self, e):
        if not e.getValueIsAdjusting():
            print 'selected row', view.getSelectedRow()

view.getSelectionModel().addListSelectionListener(SelWatch())

For a JTree, use javax.swing.event.TreeSelectionListener and view.addTreeSelectionListener(...). Standard Vision event handlers configured in the Designer are the first choice for events Ignition already exposes. Reserve raw listeners for events it does not.

Verification checks with expected readings

  1. Run print comp.UI. Expected: a ScrollPaneUI instance, matching the listed UI type.
  2. Run print comp.viewport.view.__class__. Expected: a Swing selection-capable class (table or tree). A JPanel or other container means the rows are one level deeper; repeat with .components.
  3. Call the selection method from the table above with a known row. Expected: the row highlights, and the read method returns the same index you passed.
  4. Add the listener class, then read comp.viewport.view.mouseListeners or the selection model's listener array. Expected: your listener instance appears in the array.
  5. Click a row with the mouse. Expected: the listener prints once per completed selection (getValueIsAdjusting() false), not repeatedly during a drag.
  6. With a filter active, select the same key value by view index and by dataset index. Expected: the two indices differ, and only the view index highlights the intended row.

Pitfalls that recur with scripted Swing selection

  • Assigning to a listener array property. ancestorListeners, focusListeners and the others are read-only reports. Use addXListener().
  • Listeners lost on window reopen. Closing and reopening a window builds new component instances. A listener attached to the previous instance never fires again. Register from the window's internalFrameActivated event or from a component propertyChange script so registration repeats each time the window opens.
  • Stacked duplicates. Registering from an event that fires repeatedly adds a new listener each time. Remove the previous one with the matching removeXListener(), or guard with a check against the listener array.
  • Selecting before data arrives. Calling a selection method while dataIn is empty or a binding is still loading selects nothing or throws an index error. Check comp.data.rowCount > 0 first.
  • Trusting a pasted property list. Runtime dir() output reflects the installed Ignition build and the actual child class; documentation excerpts do not.

FAQ

Why does the Ignition row selector expose so many undocumented properties?

The component extends Swing's JScrollPane, and Ignition lists every Java bean property it inherits, such as focusTraversalKeys and componentListeners. Only data, dataIn, dataOut, dataQuality, filters, appContext and dynamicProps among the listed items are Vision-specific.

Why does setting a selectedValue-style property fail on the row selector?

A dropdown exposes selectedValue as its own property, but no such property appears in the row selector's listing. Select through the viewport child instead, for example comp.viewport.view.setRowSelectionInterval(r, r) if the child is a JTable.

Why does my script listener stop firing after the window is reopened?

Reopening a window creates new component instances, and the listener you added belongs to the old one. Re-register the listener each time the window activates, and call removeXListener() first to avoid duplicates.

Why does Java Swing documentation apply to Ignition Vision components?

Vision components are Swing objects, and Jython maps getX()/setX() pairs to properties. Confirm the class with print comp.viewport.view.__class__, then read that class's Javadoc for the selection and listener methods.

Back to blog