RhinoScriptSyntax Selection Methods No Longer Working?

Hello,

I had the following script utilizing RhinoScriptSyntax to select/unselect objects in the Rhino document based on an input of GUIDs from within a grasshopper scripting component.

All was working well and suddenly the RS methods “stopped” working, returning the following error message:

  1. Solution exception:‘CustomTable’ object has no attribute ‘GetSelectedObjects’

I’ve tested in the legacy IronPython component, and the new script component in IronPython2 and Python3 to no avail. I’ve also tested in both Rhino 8 WIP and Rhino 7 (both most recent updates)

Could this be because of a recent update/conflict or am I overlooking something in my code?

Here is the code in question:

Inputs:

ID (Type Hint: GUID, List Access, Flattened)
S (Type Hint: Boolean, Item Access)
E (Type Hint: Boolean, Item Access)

import rhinoscriptsyntax as rs
import Rhino

#Select Objects By ID
def Select_Objects(ID, S):
    if S and ID:
        rs.EnableRedraw(False)  # Disable redraw to improve performance
        rs.SelectObjects(ID)
        rs.EnableRedraw(True)  # Enable redraw after selection
    else:
        return

#Handle Component Info Message
if ID:
    if S:
        Select_Objects(ID, S)
        ghenv.Component.Message = str(len(ID)) + " Selected"
    else:
        if rs.SelectedObjects():
            if E:
                rs.UnselectObjects(ID)
            else:
                rs.UnselectAllObjects()

        ghenv.Component.Message = str(len(ID)) + " IDs Listed"
else:
    if rs.SelectedObjects():
        if E:
            rs.UnselectObjects(ID)
        else:
            rs.UnselectAllObjects()
    ghenv.Component.Message = "No IDs"

Select False (error):

Select True (no error, yet no selection in Rhino either):

Thank you for your help!

this function (implemented in selection.py) calls scriptcontext.doc.Objects.GetSelectedObjects() which is not implemented.

For this to have worked previously either the function or scriptcontext has been changed in a recent release.

Regards
Jeremy

p.s. You need to unset your message for the cases where it isn’t currently required - otherwise the last given value persists. e.g.:

...
#Handle Component Info Message
ghenv.Component.Message = None
if ID:
...
1 Like

Thanks @jeremy5, I’ve tried looking into other methods such as:

Rhino.RhinoDoc.Activedoc.Objects.SelectObjects()

but this method seems to be much slower than RhinoScriptSyntax… I believe this is because the Activedoc method wants a loop to select each object one at a time, where as the previous method I could feed the selection the ID list input directly and avoid the loops.

The error I get when trying to feed the Activedoc method the ID input without looping through ID:

Runtime error (ArgumentTypeException): Multiple targets could match: Select(IEnumerable[ObjRef], bool), Select(IEnumerable[Guid], bool)

Traceback:
  line 31, in Select_Objects, "<string>"
  line 38, in script

Here’s the code using the Activedoc method which, again works, just more slowly:

import rhinoscriptsyntax as rs
import Rhino

rs.EnableRedraw(False)  # Disable redraw to improve performance

# Select Objects By ID
def Select_Objects(ID, S):
    if S and ID:
        ghenv.Component.Message = str(len(ID)) + " Selected"
        for obj_id in ID:
            Rhino.RhinoDoc.ActiveDoc.Objects.Select(obj_id, True)
    else:
        ghenv.Component.Message = None
        return

if ID:
    if S:
        Select_Objects(ID, S)
    else:
        ghenv.Component.Message = str(len(ID)) + " IDs Listed"
        if E:
            for obj_id in ID:
                Rhino.RhinoDoc.ActiveDoc.Objects.Select(obj_id, False)
        else:
            Rhino.RhinoDoc.ActiveDoc.Objects.UnselectAll()

else:
    ghenv.Component.Message = "No IDs"

rs.EnableRedraw(True)  # Enable redraw after selection

Thanks for your help and feedback!

Hi Michael,

Your original code works fine with the rs.SelectObjects(ID) part and returns the correct count for the number of guids fed into it. The error arises with the rs.SelectedObjects() part.

Regards
Jeremy

1 Like

Oddly it doesn’t work on my end even if I use rs.SelectObjects only and get rid of the rest of the code.

It appears that none of my RhinoScriptSyntax select/unselect methods are working currently

Are you saying that that is not the case on your end? and if you run the code in a script component it works for you?

No, all I know is that the first branch at if S runs and generates the correct message without showing an error.

However, if I modify the code to print the number of objects actually selected then I get None.

If I execute the same code, modified to suit, in Rhino then the objects with my guids are selected correctly.

If I’m understanding your intent then what you are trying to do is well outside my sphere of knowledge (hopefully someone better qualified will chip in) but, given McNeel have just introduced a whole load of components for accessing rhino document objects in R8 grasshopper, I wonder whether you are attempting the impossible in R7?

Thanks @jeremy5 for looking into it more! As always, I appreciate your time and help!

It was working fine in R8 last week and that’s why I find it very strange that it no longer works in R8 or R7 as it’s a method available in the Rhinoscryptsyntax API documentation from McNeel.

To the best of my knowledge outside of updating Rhino versions I have not modified my installation folders in anyway.

This leads me to think it may be a bug in recent updates involving the script editor.

I’m going to try and roll back a version or two and see if that makes a difference.

Hi Michael,

Sorry, the answer was staring me in the face! You need to set the context to the Rhino active doc before executing the Rhino commands and back to the ghdoc afterwards. Then the selection works and also the SelectedObjects no longer throws an error.

import rhinoscriptsyntax as rs
import Rhino
import scriptcontext as sc

#Select Objects By ID
def Select_Objects(ID, S):
    if S and ID:
        rs.EnableRedraw(False)  # Disable redraw to improve performance
        rs.SelectObjects(ID)
        rs.EnableRedraw(True)  # Enable redraw after selection
    else:
        return

#Handle Component Info Message
ghenv.Component.Message = None
if ID:
    if S:
        sc.doc = Rhino.RhinoDoc.ActiveDoc
        Select_Objects(ID, S)
        sc.doc = ghdoc
        ghenv.Component.Message = str(len(ID)) + " Selected"
    else:
        sc.doc = Rhino.RhinoDoc.ActiveDoc
        if rs.SelectedObjects():
            if E:
                rs.UnselectObjects(ID)
            else:
                rs.UnselectAllObjects()
        sc.doc = ghdoc

        ghenv.Component.Message = str(len(ID)) + " IDs Listed"
else:
    sc.doc = Rhino.RhinoDoc.ActiveDoc
    if rs.SelectedObjects():
        if E:
            rs.UnselectObjects(ID)
        else:
            rs.UnselectAllObjects()
    sc.doc = ghdoc
    ghenv.Component.Message = "No IDs"

HTH
Jeremy

p.s. This would be better rewritten with a pair of communicating classes (one rhino and one gh) so that the context setting can be left to each class, making it much less prone to miscoding. You’d have to switch from RhinoScriptSyntax to RhinoCommon though.

1 Like

Interesting, I had thought that was the case at one point but I only set the context to Rhino at the beginning and ghdoc at the end. I hadn’t realized I needed to incorporate that multiple times.

I’ll try this out when I’m back at my desk. Thank you!

Also what you are saying sounds really intriguing but I’m just getting started in Python. Are “Communicating Classes” the term I should be searching for to learn more about that or is it a concept of classes in general?

Thanks for the advice @jeremy5

With the code you posted, setting the context at beginning and end will work equally well. I took the design decision to wrap the context changes tightly around the Rhino commands so that adding code using the ghdoc context later would be easier.

I would look for terms like cooperation, delegation and responsibility. For more general reference, look for object-oriented design, object-oriented programming or oo and after digesting that look for patterns. Then, for a good reference to the application of these concepts in Python, grab a copy of ‘Python Object-Oriented Programming’ by Lott & Phillips.

HTH
Jeremy

2 Likes

Thanks for the research material leads!

Here’s the final code that I ended up with and is working as expected.

I really thought I had handled the script context switching previously to no avail, so when it wasn’t working I was pulling my hair out but I’m glad you had us relook at that.

Thank you for your help @jeremy5 !

Code:

import Rhino
import rhinoscriptsyntax as rs
import scriptcontext as sc

#Set Script Context To Rhino Doc
sc.doc = Rhino.RhinoDoc.ActiveDoc

# Disable redraw to improve performance
rs.EnableRedraw(False)

# Select Objects By ID
def Select_Objects(ID, S):

    if S and ID:
        if E:
            rs.SelectObjects(ID)
        else:
            rs.UnselectAllObjects()
            rs.SelectObjects(ID)
    else:
        ghenv.Component.Message = "No IDs"
        return

# Unselect Objects By ID
def Unselect_Objects(ID, E):
    # sc.doc = Rhino.RhinoDoc.ActiveDoc
    if rs.SelectedObjects():
        if E:
            rs.UnselectObjects(ID)
        else:
            rs.UnselectAllObjects()

# Check if the input parameter exists
if "ID" in globals():
    if S:
        Select_Objects(ID, S)
        # Update Message With Item Count Selected
        ghenv.Component.Message = str(len(ID)) + " Selected"
    else:
        Unselect_Objects(ID, E)
        # Update Message To Show How Many IDs Are Present
        ghenv.Component.Message = str(len(ID)) + " IDs Listed"
else:
    ID = None
    # When No IDs Are Present, Update Message Accordingly
    ghenv.Component.Message = "No IDs"

# Enable redraw after selection
rs.EnableRedraw(True)

#Set Script Context To Grasshopper Doc
sc.doc = ghdoc
1 Like

I do not know if it is related and can post here or not.

My question is:
If I select a rhino spotlight in VBScript Rhino and run this Rhino.SelectedObjects() code,

Dim arrSelectedObjects
	arrSelectedObjects = Rhino.SelectedObjects()
	If IsArray(arrSelectedObjects) Then
		Rhino.Print("Selected Object")

I presume SelectedObjects() function will return Null as if no object is selected. And actually is not printing “Selected Object” I mean, a surface is working (and printing) well, but it is not taking the light into consideration.

Any idea?

Link: The full script

Hi Alan,

Probably better as a separate topic. However: SelectedObjects will only include lights if you set an optional Boolean parameter to True. And there is a second parameter if you want to include grips.

HTH
Jeremy

P.S. If you are using ChatGPT for code suggestions, you should check the proposal against the API documentation at developer.rhino3d.com

1 Like

Hi Alan,

As Jeremy pointed out, in the SelectedObjects function it can take two arguments (booleans) that, by default are set to False and when set True will allow you to select lights and grips.

Currently you have:

rs.SelectedObjects()

You just need to change to:

rs.SelectedObjects(True, False)

Help Context:

Help on function SelectedObjects in module rhinoscript.selection:

 |  SelectedObjects(include_lights=False, include_grips=False) |      Returns the identifiers of all objects that are currently selected
 |          Parameters:
 |            include_lights (bool, optional): include light objects
 |            include_grips (bool, optional): include grip objects
 |          Returns:
 |            list(guid, ...) identifiers of selected objects
 |          Example:
 |            import rhinoscriptsyntax as rs
 |            objects = rs.SelectedObjects()
 |            for obj in objects: print "Object identifier: ", obj
 |          See Also:
 |            InvertSelectedObjects
 |            UnselectAllObjects
 |          

Cheers!

Hi Michael,

No harm done, but Alan is using VBScript with RhinoScript rather than Python with RhinoScriptSyntax (which is designed to emulate the RhinoScript functions in Python) so trivially different syntax.

And

The parameters are both optional and default to False, so no need to include the second one unless you want to set it to True.

Regards
Jeremy

1 Like

Hi @jeremy5 , thanks. Honestly I read his post too fast :smile:

Thanks for the corrections and good to know about the 2nd argument being optional!

1 Like