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

Hello,

I have a GUI that calls mesh intersection code that sometimes crashes. To prevent losing too much work, I implemented a “backup” function that saves the complete Rhino project (.3dm) and the current GUI-related values (.txt) before the dangerous operations. After a crash, if both these files are available, I load them so that my project is restored and my GUI is reseted to the right values.

My problem is that sometimes, right after the command

rs.Command("_-Open " + backupFile + " _Enter" )

Everything seems fine:

  • I see all the surfaces, curves and points (and I can pick them, etc)
  • The Layers tab shows all the layers (and I can turn them on/off)

BUT the command

rhinoscriptsyntax.LayerNames()

returns an EMPTY layer list… Then, all my other scripts fail because they cannot find their inputs in the expected layer (as rs.IsLayer(layer) returns False ). Here is a print out of the traces I have showing the problem:

First restoration (everything works as expected with lots of layers, in particular the STL one)

Entering RestoreProject...
   allLayers: ['Default', 'Layer 01', 'Layer 02', 'Layer 03', 'Layer 04', 'Layer 05']
RestorationIsPossible: True
Restoring design file: P:\PREP_DECOUPE\BackupDesign.3dm
Python Script ( ResetEngine ): _-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: ['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']
Restoring parameters file: P:\PREP_DECOUPE\BackupParameters.txt
   allLayers AFTER reading parameters: ['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']
Restoring UI
UI update finish
   allLayers AFTER UI update: ['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']

Second restoration (after Open command on same project file, no more layer!!!)

Entering RestoreProject...
   allLayers: ['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']
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: []
Restoring parameters file: P:\PREP_DECOUPE\BackupParameters.txt
   allLayers AFTER reading parameters: []
Restoring UI
UI update finish
   allLayers AFTER UI update: []

And here is the RestoreProject function that produced these traces:

 def RestoreProject(self):
        """ Restores the Rhino project from its last state along with the Heel modeling parameters.
        """
        print "\nEntering RestoreProject..."
        allLayers = rs.LayerNames()
        print "   allLayers: " + str(allLayers)
        
        if not self.RestorationIsPossible():
            return
        
        GeoUtil.ShowMessageAndContinue("Restoring design file: " + self.BkpDesignCompleteFilename(), sleeptime=2)
        rs.Command("_-Open " + self.BkpDesignCompleteFilename() + " _Enter" )
        
        allLayers = rs.LayerNames()
        print "   allLayers AFTER -Open command: " + str(allLayers)
        
        GeoUtil.ShowMessageAndContinue("Restoring parameters file: " + self.BkpParametersCompleteFilename(), sleeptime=2)
        paramFile = open(self.BkpParametersCompleteFilename(), 'rt')
        readParameters = pickle.load( paramFile )
        self.data = readParameters
        paramFile.close()
        
        allLayers = rs.LayerNames()
        print "   allLayers AFTER reading parameters: " + str(allLayers)
        GeoUtil.ShowMessageAndContinue("Restoring UI", sleeptime=2)
        self.UpdateUI()
        
        allLayers = rs.LayerNames()
        print "   allLayers AFTER UI update: " + str(allLayers)

Does anyone have an Idea of what could be going on? Is there some Rhino call that I could make that would re-parse the project structure and rediscover the layers?

Thanks

Hi,

Can you try the same with; rs.Command("_Open" ) and open the file manually?
Just tried to open a file with layerNames a couple of times and work here.

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…