Jump to content

PIO ForEachObjectInLayer traverses through function more times than expected


Recommended Posts

I'm new to Vectorscrip and ran into a problem when using the vs.ForEachObjectInLayer to update multiple selected objects.

I had more complicated code that is functioning mostly correctly but is taking way too much time to update when multiple PIO are updated at the same time.

The more PIO that are selected causes the time to be exponentially longer to execute. 

To test what was causing it to take so long to execute I wrote the simple code below to make sure it's only running through the main function one time for each PIO selected. 

Each time it goes through it displays a dialog with the (Handle) listed.

What i'm finding is that when I have one PIO selected it runs through the Main function 3 times. Each time displaying a different handle.

If I run this on two PIO items it runs through the Main function 8 times. Two times on each handle. 

 

If I change the Return to True it only runs through the Main function two times but each time it displays the same handle. And the second item is not updated.

 

Another question I have that I think might be related to this problem. 

In [ vs.ForEachObjectInLayer(main, 2, 0, 1) ] If I change the Layer Options to anything other than 1 (All layers) it doesn't work at all.  I just get a blank screen with the PIO selected and showing in the OIP. I would have expected O (Current layer) to work. That would make me think I do not have a layer selected.

 

I'm sure I'm missing something simple I dont understand. I hope what what I wrote is clear enough. 

 

import vs

def execute():
    vs.ForEachObjectInLayer(main, 2, 0, 1)


def main(h):
    vs.AlrtDialog(f'Handle: {h}') #Display the Handle each time the Main function is called.

    vs.Arc(-72, -72, 72, 72, 0, 360) #Draw something

    return False

 

Link to comment

Each time through you are creating a new object on the layer that is added to the end of the list of objects that are on the layer. Of course that is going to take a long time to run. I am surprised it ever stopped. It should have created and infinite loop.

 

ForEachObjectInLayer is great for modifying objects in the layer. It is not so good for adding new objects as each will be processed. Unless you put those objects on a different layer.

 

The FEOIL command does not make a list of the objects when it is called. It dynamically accesses the next object until it has processed everything on the layer.

 

Hope this makes sense.

Link to comment

You may also have the objects you want to parse in a different layer. 
 

Your best course of action is to have separate functions that build a list of handles and then process/modify the drawing. 
 

When the FEO functions return true, they short circuit the loop. This is useful when trying to locate a specific object, so you can stop looking once you’ve found something that meets your criteria. 

Link to comment

You can also use ForEachObjectInList() to process objects residing on another layer. This way your routines can create objects on the Active Layer while processing objects on another layer and the lists won't intermingle. Use vs.FSObject(LyrHandle) or vs.FObject(LyrHandle) to get the first handle to the objects you want to process, and pass that handle to the ForEachObjectInList() call.

 

Raymond

Link to comment

@MatthewW,

   Is your FEOIL code running inside a Plug-In object, or is it running outside of all Plug-In objects and just processing PIO objects in the drawing? Is your code causing the PIOs to regenerate?

 

   If it is running from inside a PIO and looking at objects on the outside, it very well may be running multiple times. PIOs run several times for different reasons. Where you place your FEOIL loop inside the PIO code, and how you write your code will determine how many time it will execute. There are ways to limit unwanted executions of the FEOIL loop, but for any of us to make any meaningful suggestions we will have to know a bit more about what you are doing, and what you are wanting to achieve. 

 

   If you're running your FEOIL code outside of a PIO, are you calling vs.ResetObject() or vs.RedrawAll? These can also affect performance.

 

All the  best,

Raymond

Link to comment

Thank you all for the quick response. It's much appreciated. Looking back at this I realise I should have given more information.

I created a Custom Plug-in. The type is [Point Object]. Its set to Execute Script [Immediately]. In the Script Editor it executes the python script ablove.

The tool has multiple different parameters that change different characteristics of the objects placed in the drawing. The tool would place objects that could be duplicated as many times as needed on the drawing. Then each object could be changed as needed through parameters in the OIP. Same as any of the stock tools in the Basic tool palette. 

This was all working as expected but running too slow when going back and updating parameters in the OIP

This lead me down the path to try and figure out why it's running through the code more times than is expected on each object. So I started testing with the simple code above to try and understand the ForEachObjectInLayer function.

@Pat Stanford Thanks for your explanation. I guess I really do not understand how this function works as it should be in a endless loop like you said.

@JBenghiat I will try and work out a way to get a list of handles to all objects selected and then process the changes to each of them in the list. I was thinking that is what ForEachObjectInLayer did. Is there example code in a SDK some place I can look at? 

@MullinRJ I think I answered your question now sorry for not giving enough info on my first post. I'm running the code inside the PIO. This is very simple code right now. I just want to make sure I understand what is going on before I go back and modify my full application. 

 

Thanks again.

Link to comment

Did you select the option for your plug-in to be event enabled? That would cause the script to execute multiple times.

 

A few general notes --

PIOs are designed to draw their own geometry, like a symbol. The arc you're creating is actually inside the PIO, and not the layer's you're traversing. You generally don't want to manipulate objects outside of the PIO itself. We do have some techniques for linking objects, but that requires some fairly advanced procedures.

 

Usually for Python, main() is the name of the "entry" function. That is, the last like of your code would be "main()," and all sub functions would run from there. You essentially have the naming conventions flipped in your example. This would be crucial if you were executing the code from the terminal, but in this case it just makes reading your code a little more difficult.

 

Vectorworks python implementation doesn't directly access Vectorworks drawing structure, so, for example, vs.ForEachObjectInList() refers to the Vectorworks drawing list, not a python list.

 

If you're new to python, you can see documentation on lists here: https://docs.python.org/3/tutorial/datastructures.html

Your structure would be something like:

handles = []

def add_to_list(h):
  handles.append(h)
  
def main():
  vs.ForEachObjectInLayer(add_to_list...
                          
  for h in handles:
      vs.DoSomething(h)
                         
main()
                          

 

That guarantees that if you change the drawing structure, you are only iterating through the objects present when you first run the command.

  • Like 1
Link to comment

Thanks @JBenghiat. I might need a min to digest all this info but when I go to the Plug-in Manager and [Edit Definition]. Under the [Options] tab I do not have anything checked under Execution Options. 

 

Where I was using vs.ForEachObjectInList is when trying to update Parameters and Data Records. They require a handle to the object that it should update. But this is very inefficient because I guess it runs the whole script for every object that is selected when a change is made in the OIP. Effectively its doing the same FEOL operation again and again for all the PIO being updated. 

Is there a function that gives you the handle to the current object being updated? Not just the first object in the list. I can't seem to find one. I'm sure I'm just overlooking the obvious.

If not I'm thinking of only run the FEOL on the last selected object this way it keeps from updating all the objects every time it goes through the selected object list as a workaround.

I appreciate all your help on this. I know it's probably frustrating dealing with newbies like myself fumbling around trying to figure this out. 

Link to comment

Please step back and tell us more about what you are actually trying to do.

 

It sounds like you have two different things going on.  

 

1. A point type PIO that may be placed in the drawing multiple times.

2. A tool that is editing one or more of those PIOs

 

So what are you really trying to do and what is having the slow reset time?

Link to comment

A couple principals that may help clarify things:

 

PIO's regenerate their geometry on every reset, so if we think of them as dynamic symbols, on each reset they clear and re-draw the symbol contents

 

A PIO can access it's own parameters a constants by adding vs.P to the parameter name

 

https://developer.vectorworks.net/index.php?title=VS:GetCustomObjectInfo will get the handle of the currently regenerating PIO

 

If you need to affect objects outside the PIO, you would either do this with a separate menu command or by pressing a button in the OIP so the command only happens once. The latter requires diving into event-based PIOs.

Link to comment
  • 4 weeks later...

Thanks for all your help on this. I figured out what the problem was after looking through the documentation more. I realized that I was struggling with getting the handle to the current object being modified. I went down the wrong road with trying to use ForEachObjectInLayer to get a list of objects and then update them. 

The simple thing that made this easy is when I used (GetCustomObjectInfo) to get the (objectHand) this returned the handle to the current object being updated when updating multiple objects at the same time. It was simple after I found this function. Sorry about all the back and forth. But I did learn a lot along the way.

 

ObjHandle = vs.GetCustomObjectInfo()[2]

 

 

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...