[python] Nested blocks as nested lists?


(IVELIN PEYCHEV) #1

Is there a way I can get the nested blocks as nested lists?

So far I see I can get all blocks as a single list.


(Dale Fugier) #2

Does this help?

SampleDumpInstanceDefinition.py

– Dale


(IVELIN PEYCHEV) #3

Thanks Dale,

This is nice!
I’ll see how I can make this work.

If I may propose one tiny change to this script instead of Writing line to print the result so that one can review it inside the editor :wink:

PS: This is the first time I see while loop in use with Rhino that doesn’t cause crash :smiley:. All my attempts so far have ended with crashes. This is the general reason why I asked the question, because all I could think about was using While and whenever I use While I crash Rhino :stuck_out_tongue_winking_eye:


#4

In a while True loop, you need to make sure there is a definite point where you break out of the loop, otherwise it will run forever (and crash Rhino). There are a bunch of ways to do this, but you absolutely have to make sure your logic is ironclad and can’t fail.

When testing stuff I usually put in a safety - count the loops and automatically break out if the loop count goes over some number. You can do this by adding a conditional to the while loop:

count=0
while count<1000:
    #do your thing
    count+=1
if count>=1000: print "Safety limit exceeded!"

or in the loop

count=0
while True:
    #do your thing
    count+=1
    if count>=1000:
        print "Safety limit exceeded!"
        break

Once you’re sure you are exiting the loop before you hit the safety point, you can remove it.


(IVELIN PEYCHEV) #5

Good idea, thanks! :slight_smile:


(Nathan 'jesterKing' Letwory) #6

Not necessarily crash - just hang (when on the main thread). It is doing all the time something, just no longer responsive. But yeah, not the ideal situation.


#7

@ivelin.peychev, just put this on top of your while loop so you can cancel with ESC:

while True:
    if scriptcontext.escape_test(False): return
    # your stuff below this...

_
c.


(IVELIN PEYCHEV) #8

This doesn’t work @clement. I’ve tried that. After Rhino becomes non-responsive even escape doesn’t work. This only works when used inside Grasshopper and only due to the fact that Grasshopper has a mechanism to stop the calculation on errors.


#9

Safety first indeed:


(Nathan 'jesterKing' Letwory) #10

Of course make sure your exit condition isn’t in a dead branch inside that loop :slight_smile:


(IVELIN PEYCHEV) #11

God damn!
Ain’t no security that protects Rhino from crashing from my scripts :rofl:

Can someone point me out the issue. This thing below crushes Rhino completely. :stuck_out_tongue_winking_eye:

import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino
import time

blocks_ids = rs.GetObjects("Select Block(s): ",rs.filter.instance)

ts = time.time()
rs.EnableRedraw(False)

def step_layerizator(block_id):
    block_name = rs.ObjectName(block_id)
    rs.AddLayer(block_name)
    sub_blocks_ids = rs.BlockObjects(block_name)
    print sub_blocks_ids
    if len(sub_blocks_ids) > 0:
        for sub_block_id in sub_blocks_ids:
            if sub_block_id != "":
                sub_block_name = rs.BlockInstanceName(sub_block_id)
                
                sub_sub_blocks_ids = rs.BlockObjects(sub_block_name)
                print sub_block_name
                
                rs.AddLayer(name=sub_block_name, color=None, visible=True, locked=False, parent=block_name)
                step_layerizator(sub_block_id)

for block_id in blocks_ids:
    iterations = 0
    while True :
        step_layerizator(block_id)
        iterations += 1
        if iterations > 250:
            break

"""
for block_id in block_uuid:
    block_name = rs.ObjectName(block_id)
    rs.AddLayer(block_name)
    sub_blocks_ids = rs.BlockObjects(block_name)
    print sub_blocks_ids
    if len(sub_blocks_ids) > 0:
        for sub_block_id in sub_blocks_ids:
            if sub_block_id != "":
                sub_block_name = rs.BlockInstanceName(sub_block_id)
                
                sub_sub_blocks_ids = rs.BlockObjects(sub_block_name)
                print sub_block_name
                
                rs.AddLayer(name=sub_block_name, color=None, visible=True, locked=False, parent=block_name)
                if len(sub_sub_blocks_ids) > 0:
                    for sub_sub_block_id in sub_sub_blocks_ids:
                        if sub_sub_block_id != "":
                            print sub_sub_block_id
                            sub_sub_block_name = rs.BlockInstanceName(sub_sub_block_id)
                            sub_sub_sub_blocks_ids = rs.BlockObjects(sub_sub_block_name)
                            print sub_sub_block_name
                        
                            rs.AddLayer(name=sub_sub_block_name, color=None, visible=True, locked=False, parent=sub_block_name)
"""


te = time.time()
print "Elapsed time is {:.2f}".format(te-ts)

UPDATE: The commented code works rather good but I can’t figure out how to make it work if I don’t know the number of levels of the nested blocks.


#12

Hi @ivelin.peychev, i am using this in +200 scripts and it works if ESC is pressed when a new loop starts. It also works in multiprocessed scripts. I asume that you’re code below this line is hanging and therefore you never reach the point (the next loop) where the check for ESC is happening.

recursion…

_
c.


(IVELIN PEYCHEV) #13

:smiley: insightful. I know that, but I don’t want to crash and restart rhino every time I wanna test what I have done.

Good for you. I’m a noob and I can’t make it work. So I try to avoid While loop with Rhino as much as I can.


#14

If you hold ESC for some time, does it stop ?

import Rhino
import scriptcontext

def DoSomething():
    counter = 0
    message = "Hold ESC to stop: {}"
    
    while True:
        if scriptcontext.escape_test(False): break
        Rhino.RhinoApp.SetCommandPrompt(message.format(counter))
        counter += 1
        if counter > 10000: return
        
    print "ESC pressed at {}".format(counter)
    
if __name__=="__main__":
    DoSomething()

_
c.


(IVELIN PEYCHEV) #15

This particular code works but Rhino is not overloaded enough to become non-responsive. When that happens no click or button press matters.


#16

It can only check for the key press / hold down if it is not in the middle of something eg. a boolean or split operation. Sometimes it helps if you’re adding a few milliseconds above the key check using rs.Sleep(10). Btw. if you want to cancel the while loop anyway and set it up with a counter, you can just use this:

counter = 0
while counter <1000:
    # Do something here
    
    # increase the counter
    counter += 1

_
c.


(IVELIN PEYCHEV) #17

Or Rhino, RhinoPythonEditor, Grasshopper, RhinoPythonEngine, etc. should all run as separate processes. :wink:


#18

… recursion example (rvb) you might try this. Also i found this by @Helvetosaur but have not tested it.

edit, just seen that the second post in this thread also has recursion example.
_
c.


(IVELIN PEYCHEV) #19

I’ve tested all of these, but as I said, when rhino gets overloaded to a non-responsive state none of them matter.

There are two options that slightly increase the chance of recovery. One is, as you said above, to pause the PythonInterpreter, the second is to make Rhino wait for one operation to complete before launching the other:
Rhino.RhinoApp.Wait()


#20

Something I also try to avoid if possible, but it is the only way to deal with items that are nested to an undetermined depth. There are two situations concerning the Rhino interface that could need this, layers with sublayers and nested blocks. Layers are relatively easy as there is only one type of entity we are dealing with - a layer name or ID - whereas blocks are more complex, as they could contain any number of objects including other blocks.

In any case, it’s a bit difficult to wrap your head around the concept. As the old joke goes-

“In order to understand recursion, you first need to understand recursion” :stuck_out_tongue_winking_eye:

Basically it’s a subroutine that calls itself (inside itself). If you imagine that a nested list has a structure like a tree with branches, on each loop the script crawls up one branch until it gets to the next one that branches off from it, then goes up that one to the next branch point etc. At some point, the script gets to the end of a twig that has no other branches. That is the critical point. If you don’t have some code in there to detect that you got to the end of a branch structure, it will just stay there and continue to run infinitely (and hang Rhino in this case).

So usually you have a line that says that if no more branches are found in the “up” direction, to break out of the current loop (but not exit the script). That has the effect of pushing the script back down to the previous level and start looking for new branches on that level. When those are all found and done, then it goes back down another level, and so forth. You can see in this way that the entire tree structure is then covered. For example, in the case of parsing a nested layer tree, on every run it would be checking if the current layer has any children. If not, then it needs to break out, as that is the end of that particular branch.

While you are developing this type of stuff, it’s useful to have a small file with only a few elements to test, put in print statements to tell you at what branch level you’re at, and have a safety valve in case you do get stuck in an infinite loop anyway.