Python - Get Selected On Selection Change Event Handler

Hello,

I’m trying to get the currently selected rhino object from the Rhino document in real-time.

If I use this script with a trigger attached to the component it works fine of course:

import Rhino
import rhinoscriptsyntax as rs

sel_objs = []

def get_selected_objs():
    for obj in Rhino.RhinoDoc.ActiveDoc.Objects.GetSelectedObjects(False,False):
        geo = rs.coercegeometry(obj)
        sel_objs.append(geo)
    return sel_objs

get_selected_objs()

a = sel_objs

However, I’m trying to use event handlers so that anytime a selection change occurs, i get the most current selected object in real time. I don’t want to use the trigger as that is computationally wasteful.

I just can’t seem to get it to work without return an empty output result.

Any help is greatly appreciated!

Thank you!

Full Code:

import Rhino
import rhinoscriptsyntax as rs

class SelectionChangeEventHandler():
    # Initialize sel_objs as an empty list
    sel_objs = []

    def __init__(self):
        Rhino.RhinoDoc.SelectObjects += self.get_selected_objs
        self.Enabled = True
    
    def Disable(self):
        Rhino.RhinoDoc.SelectObjects -= self.get_selected_objs
        self.Enabled = False
    
    def IsEnabled(self):
        return self.Enabled
    
    def get_selected_objs(self, sender, e):
        self.sel_objs = []  # Clear the previous selected objects
        for obj in Rhino.RhinoDoc.ActiveDoc.Objects.GetSelectedObjects(True, False):
            geo = rs.coercegeometry(obj)
            self.sel_objs.append(geo)
        
        # Assuming objs is a variable representing the output
        objs = self.sel_objs  # Update objs with selected objects
        return objs

if __name__ == '__main__':
    # Create an instance of the event handler to start listening for events
    handler = SelectionChangeEventHandler()

event_handler_c.gh (13.9 KB)

Check this.

2 Likes

Thanks @martinsiegrist but that doesn’t seem to do anything on my end?

all of the panel outputs are empty on every component regardless of whether I select any object, deselect, or have nothing selected

It works here.

Oh you know what I just remembered that when modifying event handlers, you need to restart Rhino for them to work.

Let me try that. Thanks for testing on your end!

1 Like

I used it for a while but yesterday I went back to referencing an input object with python. I’m doing a lot of control point editing on meshes and need the updated mesh in Grasshopper but the select / deselect event handler does seem to detect sub object selections.

1 Like

Works perfectly thank you!

I’m also interested in sub-object selection and control-point manipulation so thank you for sharing the script and for sharing that information.

I really appreciate it!

1 Like

Hi @martinsiegrist ,

I added this line:

if (!guids.Contains(rhObj.Id))

above:

guids.Add(rhObj.Id);

Because while the guids were clearing on deselecting, anytime i moved an object in rhino it append a duplicate guid to the guids list (A out)

Does that happen on your end?

Full Code:

// Grasshopper Script Instance
//#! csharp
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;

using Rhino;
using Rhino.Geometry;

using Rhino.DocObjects;
using System.Linq;

using Grasshopper;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Data;
using Grasshopper.Kernel.Types;

public class Script_Instance : GH_ScriptInstance
{
    
  private void RunScript(out object A)
  {
    if(!_init)
    {
      Rhino.RhinoDoc.SelectObjects -= RhinoDocOnSelectObjects;
      Rhino.RhinoDoc.SelectObjects += RhinoDocOnSelectObjects;
      Rhino.RhinoDoc.DeselectObjects -= RhinoDocOnSelectObjects;
      Rhino.RhinoDoc.DeselectObjects += RhinoDocOnSelectObjects;
      Rhino.RhinoDoc.DeselectAllObjects -= RhinoDocOnDeselectAllObjects;
      Rhino.RhinoDoc.DeselectAllObjects += RhinoDocOnDeselectAllObjects;
      
      _init = true;
    }
    A = guids;

  }
  private bool _init = false;
  List<Guid> guids = new List<Guid>();
  private void RhinoDocOnSelectObjects(object sender, RhinoObjectSelectionEventArgs e)
  {
    foreach(var rhObj in RhinoDoc.ActiveDoc.Objects.GetSelectedObjects(true, false))
      
      if (!guids.Contains(rhObj.Id))
      guids.Add(rhObj.Id);

    Component.ExpireSolution(true);
  }
  private void RhinoDocOnDeselectAllObjects(object sender, RhinoDeselectAllObjectsEventArgs e)
  {
    guids.Clear();
    Component.ExpireSolution(true);
  }
}
1 Like

It took a moment but after selecting multiple objects I see what you mean.

Great improvement.

1 Like

On the GHPython SDK mode side of things, one can reliably (in my experience at least) subscribe and unsubscribe to events using the __enter__ / __exit__ methods:

Edit: This appears to work if I understood the problem correctly:


230929_GetSelectedRhinoGeometry_00.gh (4.0 KB)

2 Likes

Awesome, thanks @AndersDeleuran !

1 Like

I happen to need this type of feature for a user interaction experiment that I’m trying to develop, and your code has helped me understand in a level, how to trigger a grasshopper component through events. What I need to know is why does it sometimes trigger more than two times once you select and object.

Here is a example file that I use to track the times a component triggers:
Select2Update.gh (3.5 KB)

If I find anything I’ll post here to update my contribution to your code.

Nice script. It is possible to change this script to reference GUIDs instead of Geometry?

Do you mean something like this:


240207_GetSelectedRhinoGeometry_00.gh (4.5 KB)

4 Likes

Yes, it is working great. Thank you.

1 Like

I do have two more small feature requests if possible.

One of them it is to add a Reset button as input (I can use this to quickly enable/disable the component), and another one it is to add a small delay between calculating the selection. The second feature it is very useful when dealing with a lot of objects and want to isolate the selected ones in Rhino, my viewport freezes for a few seconds as seems that it tries to recompute multiple times the selection.

My goal it is to replace the HumanUI “Objects by Selection” which literally freezes for a few good minutes my viewport if I try to isolate the selected objects.

That’s a really nice script Anders! This will help me a lot, thanks!

1 Like

Hello Anders,
thank you for the script you provided!
With Rhino 7 i had no problems, but when i tried to copy and paste it into a new Python 3 component, which got released with Rhino 8, i get the Error shown in the picture.
I think it has something to do with the way the list is structured. As you can see the two Guids are listed in the same entry.
Also there apparently is something different with the event handlers, because when i change the selection, there is no change in behaviour.
Do you have an idea why and how i can make it work again?
(If not obvious, i am quite new to working with Python in Grasshopper)


Get Selected Objects.gh (10.9 KB)

1 Like

That script relies on the GHPython component running in SDK mode. I’m not sure what the current status is with the new script editor supporting this feature, but either way I’d suspect it might require the component to be running IronPython (i.e. not CPython, which seems to be the case with the component in your screenshot).

Ah, i see.
I tried with the IronPython Component and got no Errors this time!
The event handlers still don’t seem to work though, as there is no updating when i change the selection.
If this is also just a matter of missing features, i think i will just stick to the old Python Component for now.
Thanks!