Stepping through objects on the screen

Is there any easy way to iterate through the objects on a screen? I have a screen with 300+ objects and I’m currently using nested ‘for’ loops and building the name of each object with code like

for i in range(1,59): for j in range(1,6): objName="Object_" + str(i) + "_" + str(j) object=event.source.parent.getComponent(objName) print object.name

It would be nicer if you could do something like

window=fpmi.gui.getParentWindow(event) for object in window.rootContainer: print object.name

Yes, the only trick is that you’ll need to have some sort of naming convention so that as you iterate through the components, you know which component is a container, and recursively iterate through that container as well. Of course, if all of your components are in the top level (root container) then this doesn’t apply. Seeing as your example has all components in the root container, I’ll assume this is the case.

So, in this case, try something like this:

root = event.source.parent for comp in root.components: print comp.name

So, the trick here is that containers define a field called “components” that is a list of all of their children. If you wanted to recursively descend through all containers, name them all with a prefix like “cont_” and then…

[code]def processContainer(container, self):
for comp in container.components:
print comp.name
if comp.name.startswith(“cont_”):
self(comp, self)

root = event.source.parent
processContainer(root, processContainer)[/code]

Note we have defined a function here, and then pass that function into itself for recursion. (There may be a cleaner way to do this, any Python experts out there?)

Hope this helps,

Ouch - recursion makes my head hurt!

I tried putting the following code on a button to clear the text of a large number of labels:

root = event.source.parent for comp in root.components: if comp.name.startswith("ISO_",0,4): comp.text=""

This worked well. I then tried to put it in the picture’s InternalFrameActivated event so that the labels were cleared when the picture was displayed. (I want to do this rather than stopping the picture from being cached, as it’s >3MB and takes too long to load each time it’s opened.) However I’m not sure how I can reference the picture from within the InternalFrameActivated script.

Also, is there any documentation showing the object model within PMI?

How are these labels texts set after they are cleared? It would probably be easier to just bind their text property to the expression “” (empty string). That would clear them out when the window was opened…

Anyhow, to put this script on a window you’d do this:

root = event.source.rootContainer for comp in root.components: if comp.name.startswith("ISO_",0,4): comp.text=""

The object model is pretty simple. Windows have a property called .rootContainer.

All containers have a .components that return a list of their components, and a .getComponent(‘name’) function that finds a component with a given name.

All components (containers are components) have a .parent that points to their parent, and accessors for all of their properties (like .name). Note that a root container’s .parent isn’t the window. Use fpmi.gui.getParentWindow to find a parent window.

This object model is documented (somewhat loosely) in the “Scripting Functions” sections on each component’s doc page in the technical reference.

also, using the term “picture” for “window” here is going to be very confusing for us :slight_smile:

Hope this helps,

Yeah, what Carl said…

Possibly easier yet, you can clear all the labels when the window opens by simply changing the window cache policy to never and saving your project with blank values (or any other default) in the labels.

edit - OK, I’m a little slow on the caching…it was for the benefit of other readers…really… :question:

What are you doing to create a 3 meg window? :open_mouth:

:slight_smile:

The window is an overhead view of a warehouse containing road containers. There are 58 stacks of containers in stacks of between 4 and 5 high. I represent each container with labels with an etched border. If a container is in a position I change the text of the label to the container number. The window is populated from a dataset which is populated in the internalFrameActivated event. An expression in each label changes the label background colour to make it look like a container is there. Clicking on a container displays a popup window with information about that container.

Along with stack and height numbers and page headings etc., there are 396 labels in the window… Initial load performance is a bit slow, but speed of populating the screen is great, especially once the picture is cached.

Wow! If some asked me how quickly you could refresh a screen with that many objects I would have had no idea.

Have you tried populating all the labels text from that dataSet directly with an expression binding? This would require polling on the dataSet, but would automatically refresh that component whose value in the dataSet changed on a value change.

Also, 2.2 brings in Jython gui functions that allow you to move components. I don’t know if it’d be possible for this page, due to not being able to have multiple instances of objects on the screen, but you could use that approach - if, for example, you never expected to have more than 20 containers on the screen at a given time.

Sounds to me like it’s working fine… don’t fix what ain’t broke!

In all seriousness, I’ve seen various projects like this with screens with > 400 active components on them. Like the poster reported, initial screen load is a bit sluggish, but once the screen caches, data updates are very very fast. The typical approach here is to have one or more datasets on the window that have your data (they can either be polling or run-once), and then bind component properties using a lookup function. There shouldn’t need to be any scripting involved.

But, like I said, it sounds like you’ve got it working fine - there is more than one way to skin a cat, as the saying goes.

This is exactly what i need, how can I check type with the returned info. I tried using type() and isinstance() but it does not recognize 'com.inductiveautomation.factorypmi.application.components.BasicContainer'

Any suggestions?

[quote=“Carl.Gould”]How are these labels texts set after they are cleared? It would probably be easier to just bind their text property to the expression “” (empty string). That would clear them out when the window was opened…

Anyhow, to put this script on a window you’d do this:

root = event.source.rootContainer for comp in root.components: if comp.name.startswith("ISO_",0,4): comp.text=""

The object model is pretty simple. Windows have a property called .rootContainer.

All containers have a .components that return a list of their components, and a .getComponent(‘name’) function that finds a component with a given name.

All components (containers are components) have a .parent that points to their parent, and accessors for all of their properties (like .name). Note that a root container’s .parent isn’t the window. Use fpmi.gui.getParentWindow to find a parent window.

This object model is documented (somewhat loosely) in the “Scripting Functions” sections on each component’s doc page in the technical reference.

also, using the term “picture” for “window” here is going to be very confusing for us :slight_smile:

Hope this helps,[/quote]

Did you import com.inductiveautomation.factorypmi.application.components.BasicContainer before trying to use it?

Example:

from com.inductiveautomation.factorypmi.application.components import BasicContainer
if isinstance(myObject, BasicContainer):
	print "it is"

Best,

1 Like