LayerNames returns empty list after Open command launch from modeless dialog?

Hi,

Is the following script what you had in mind?

import rhinoscriptsyntax as rs
def OpenProjectTest():
        allLayers = rs.LayerNames()
        print "allLayers before Open command: " + str(allLayers)
        
        desigbBkpFilename = "P:\PREP_DECOUPE\BackupDesign.3dm"
        rs.Command("_-Open " + desigbBkpFilename + " _Enter" )
        
        allLayers = rs.LayerNames()
        print "allLayers after Open command: " + str(allLayers)
                
        rs.Command("_-Open " + desigbBkpFilename + " _Enter" )
        allLayers = rs.LayerNames()
        print "allLayers after second Open command: " + str(allLayers)

OpenProjectTest()

Running this small program from the Python code editor, we see everything works:

Output of this code shows its working as expected:

allLayers before Open command: ['Default', 'Layer 01', 'Layer 02', 'Layer 03', 'Layer 04', 'Layer 05']

allLayers after Open command: ['Default', 'STL', '104564D Podoscan', '0', '104564D_Contour_Data4941985', 'Metatarsal_1', 'Metatarsal_2', 'Metatarsal_3', 'Metatarsal_4', 'Metatarsal_5', 'AlignmentPoint_1', 'AlignmentPoint_2', 'AlignmentPoint_3', 'AlignmentPoint_4', 'AlignmentPoint_5', 'AxisLines', 'Forefoot_Contour', 'Medial_Edge', 'Lateral_Edge', '104564D_Contour_Data4941985::Heel_Contour']

allLayers after second Open command: ['Default', 'STL', '104564D Podoscan', '0', '104564D_Contour_Data4941985', 'Metatarsal_1', 'Metatarsal_2', 'Metatarsal_3', 'Metatarsal_4', 'Metatarsal_5', 'AlignmentPoint_1', 'AlignmentPoint_2', 'AlignmentPoint_3', 'AlignmentPoint_4', 'AlignmentPoint_5', 'AxisLines', 'Forefoot_Contour', 'Medial_Edge', 'Lateral_Edge', '104564D_Contour_Data4941985::Heel_Contour']

I already did this experiment before posting in the hope of pinpointing what went wrong in a smaller context but could not reproduce the problem I had in my bigger GUI module. Do you have any other ideas that I could test? Is there a way to get a view of the code for the Open command?

Thanks

Could it be related to the following post?

Does anybody know if this problem has been fixed?

Thanks

Hi Bruno

From RhinoCommon’s RhinoApp.RunScript() documentation:

Remarks
Rhino acts as if each character in the script string had been typed in the command prompt. When RunScript is called from a “script runner” command, it completely runs the script before returning. When RunScript is called outside of a command, it returns and the script is run. This way menus and buttons can use RunScript to execute complicated functions.

I don’t understand what that means exactly, but … seeing that in the restoring script

rs.LayerNames()

is called right after

rs.Command( ‘Open’ )

… which calls the RhinoApp.RunScript() method,
what about trying to wait a little bit before getting the layer list ?

Just a shot in the dark …

maybe… try putting rhino.rhinoapp.wait() after rs.Command( ‘Open’ ).

Rhino.RhinoApp.Wait():
Pauses to keep Windows message pump alive so views will update and
windows will repaint.

Hello,

I tried your suggestions (even added a sleep) and inserted the following code between the call to Open and the LayerNames() query:

# Try giving Rhino some time to finish its stuff.
time.sleep( 3 )
Rhino.RhinoApp.Wait()  

but I still get an empty layer list! Other ideas?

I thought it might have been related to the following bug:


http://mcneel.myjetbrains.com/youtrack/issue/RH-30434

but it seems to be fixed in my Rhino (Version 5 SR12 32-bit (5.12.50810.13095, 2015-08-10)) as I cannot reproduce the bug with the script anymore.

Hello,

Is there a way to get very detailed logs of what Rhino is doing while restoring a project? There is a lot going on when you run a rs.Command that does not leave traces. Is there a way to inspect the inner data structures of the project at runtime and find what isn’t working in my special case? My bigger GUI script still exhibits the problem but I’m unable to reproduce it in a smaller test script.

Could there be some sticky global data that could remain somehow corrupted by some Open command used on the same project twice (like cached information synchronization problem, etc.)

This behavior is still troubling me very much so any help, cue, etc is welcome!

Sorry, no.

Can you provide a simple, sort, easy-to-understand script that replicates the problem?

Sleep is measured in milliseconds so maybe try 500 or so.

No cigar! The Python 2.7 time.sleep() function I call really takes its argument in seconds (I really see the application hang for 3 seconds with this call). Thanks, was worth checking anyway.

Hello Dale,

I tried extracting the restoration function in a stand alone script that I could unit test but I cannot reproduce the bug and the restoration works multiple times. Looking for differences, here is what I found…

My main GUI script is based on Mark Meier GUI toolkit. I have hooked the restoration call to a Restore button with the following callback:

  def Restore_OnButtonPress(self, sender, e):
    print "\nEntering Restore_OnButtonPress..."
    allLayers = rs.LayerNames()
    print "   allLayers: " + str(allLayers)
    self.RestoreProject()

When pressing this button, here is what I get…

Entering Restore_OnButtonPress… All layers are still present
allLayers: [‘Default’, ‘STL’, ‘104564D Podoscan’, ‘0’, ‘104564D_Contour_Data21528673’, ‘Metatarsal_1’, ‘Metatarsal_2’, ‘Metatarsal_3’, ‘Metatarsal_4’, ‘Metatarsal_5’, ‘AlignmentPoint_1’, ‘AlignmentPoint_2’, ‘AlignmentPoint_3’, ‘AlignmentPoint_4’, ‘AlignmentPoint_5’, ‘AxisLines’, ‘Forefoot_Contour’, ‘Medial_Edge’, ‘Lateral_Edge’, ‘104564D_Contour_Data21528673::Heel_Contour’]

Entering RestoreProject… Creating New project
Command: _-New
Name of the Rhino template file to open ( None Browse ): N
allLayers:
RestorationIsPossible: True
Restoring design file: P:\PREP_DECOUPE\BackupDesign.3dm
Command: _-Open
Name of the file to open ( UpdatePromptUpdateBlocks=Yes Browse ): P:\PREP_DECOUPE\BackupDesign.3dm
File “P:\PREP_DECOUPE\BackupDesign.3dm” successfully read
Command: _Enter
allLayers AFTER -Open command: [] (No more layers from rs.LayerNames())

The only difference that I see between the GUI setup and the unit test is that the GUI calls RestoreProject as an object method where in the unit test I extracted the code as a free function. Now, my main expertise is in C++, maybe there is something going on in Python that I don’t get?

I don’t know what to do with this new piece of the puzzle but maybe someone more knowledgeable would have an idea?

Thanks

Hi Bruno,

Does your Python UI happen to be modeless? That is, when you UI is displayed, can you run Rhino commands, click toolbar buttons, etc.?

– Dale

Yes, I can use Rhino tools, run other commands, etc. while my GUI is alive. Of course, when it triggers my other scripts, I cannot run commands at the same time but once a processing is done, I can interact again with Rhino.

My suggestion would be to try to emulate what rs.LayerNames is doing, and to see what is going wrong and where.

You can lookup all the python code for rhinoscriptsyntax here https://github.com/mcneel/rhinoscriptsyntax (use the search function on the top of the page!)

The LayerNames function does this:

def LayerNames(sort=False):
    rc = []
    for layer in scriptcontext.doc.Layers:
        if not layer.IsDeleted: rc.append(layer.FullPath)
    if sort: rc.sort()
    return rc

It uses the global variable scriptcontext.doc, which is nothing more than Rhino.RhinoDoc.ActiveDoc, so you may want to use that instead.

Thanks for the tip! I will look at this code and try to see where the problem is coming from.

This may be the problem.

Calling rs.Command, which just call RhinoCommon’s Rhino.RhinoApp.RunScript function, to run a Rhino command works differently in modeless UI than it does when called from a script runner command.

When you run simple Python scripts that call rs.Command, each command is executed in serial. This is because the RunPythonScript command is script runner command.

But without something that I can run here, this is just speculation…

Hi,

This is a little obscure to me but I think you pointed me in a promising direction. I read the link you pasted about script runner command and I get the idea but I don’t see exactly how this concept translates in Python scripts and how I can work around this behavior in Python? Is there any other documentation about this subject more Pythony?

Anyway, now that I have a lead that it can be caused by modeless UI, I will try to build a small UI example reproducing the problem…

Did not find anything different by emulating the rs.LayerNames() function. After reading Dale’s post about rs.Command working differently if called from modeless UI, I think that’s a very likely explanation at least worth testing…

Hello,

I manage to put together a small UI named ModelessUILayerNamesTest.py showing the problem. It depends on Meier_UI_Utility.py so you need to put it into your Rhino search path. When you click its single Load Project button, it prompts you to open the SmallMultiLayerD20.3dm project having 4 layers with a single icosahedron on each one.

Opening this project, you will see traces in the console showing that the rs.LayerNames() function returns an empty list even though the project loaded without error and everything else seems to work.

I hope this small self-contained example will help you suggest me some work around for my modeless despair :wink:

Thanks!

UIModelessTest.zip (22.5 KB)

Hi Bruno,

Your sample was the key for figuring out what is going on - thanks for spending the time to type this up.

I’ll try my best to explain what is going on and how you can fix your code.

First, if you open the Layers.py file included with Rhino, you will see the following definition for the rs.LayerNames method:

def LayerNames(sort=False):
    rc = []
    for layer in scriptcontext.doc.Layers:
        if not layer.IsDeleted: rc.append(layer.FullPath)
    if sort: rc.sort()
    return rc

Notice the scriptcontext.doc statement. If you open scriptcontext.py, again included with Rhino, you will see this:

'''The Active Rhino document (Rhino.RhinoDoc in RhinoCommon) while a script
is executing. This variable is set by Rhino before the execution of every script.
'''
doc = None

When you first run your script, scriptcontext.doc is set to the active document.

Your script then calls rs.Command and opens a new document. The document referenced by scriptcontext.doc is no longer valid.

You then call rs.LayerNames, which tries to get layer names from an invalid document. Thus, nothing is returned.

To fix this problem, add the following statement immediately after opening a new document:

scriptcontext.doc = Rhino.RhinoDoc.ActiveDoc

Now, scriptcontext.doc references a valid document and all rs methods will work.

@stevebaer, @Alain, I’m not sure you’ve run into this before (I certainly haven’t). Something to bookmark I guess…

Let me know if there are any questions.

– Dale

1 Like