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:
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"
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
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.
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.
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.
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?
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.
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.
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
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.
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
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
|
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.