Python event handling

Hey all,
Im writing on a GHPython script in Rhino 5 that I want to re-run when objects in the active rhino doc are transformed (moved or rotated mainly). I managed to get this to work (but not efficiently) using the following code that @AndersDeleuran posted on the forum a while ago:

def addRhinoDocEvent(sender,e):
    """ Update this GH component """
    ghenv.Component.ExpireSolution(True)

#Check to see if the callback is already assigned
if "eventID" not in globals():
#    # Instance of function to call on event
    eventID = addRhinoDocEvent
#    # Assign function to a rhino doc event. 'AddRhinoObject' appears to trigger events for object transformations as well?
    Rhino.RhinoDoc.AddRhinoObject += eventID

My main issue with the code is that if i move or rotate several items at once in the active rhino doc, the GH script is ran once for every event flag. Ideally it should only run once.

Is there something like a queue for commands that I can access to see all the coming actions to the Rhino doc or possibly some way to check when there are no events/idling (seems RhinoApp.Idle and RhinoDoc.AddRhinoObject are independet of one another?) Also if anyone has a tip on how I can wait for say 1ms to see if any more events happen, and if not rerun my component after that?

Very greatful for any pointers on how to approach this and/or where I can find more information on how to work with events in Rhino!

Best
/ Jakob

Hi Jakob,

when a single object is transformed in the rhino document without copying it, these main events are fired in order:

Rhino.RhinoDoc.BeforeTransformObjects
Rhino.RhinoDoc.ReplaceRhinoObject
Rhino.RhinoDoc.DeleteRhinoObject
Rhino.RhinoDoc.AddRhinoObject

In case of multiple transformed objects without copying them, the BeforeTransformObjects event gets fired once for all transformed objects, but all events following are fired once for every transformed object.

Since you’re only interested in updating your component once after the transform has been performed i would setup two events, first the BeforeTransformObjects event which could give you the objects and the transform and second the Rhino.RhinoApp.Idle event which gets fired multiple times after the transform has been completed.

However there are two things to consider, first is this bug in Rhino 5 which prevents you from getting e.Objects() from the event and second, your Rhino.RhinoApp.Idle event will be fired hundrets of times in a row if you do not exit it. You might check if you subscribed to the events already as you do above and additionally use the BeforeTransformObjects event to set a global flag to True that you want to update the component in the Rhino.RhinoApp.Idle event. Then in the Idle event, check your global flag, update your component if it is True, then set this flag to False so the update is only performed once.

btw. there is much more to consider when using events. Eg. Undo and when a new document is opened, the events are still running…
_
c.

4 Likes

I never did implement much of this due to similar issues, so I’d also be interested in learning more about this subject and how you’re getting along.

1 Like

I don’t have much to add other than what @clement nicely explained. Also, I would highly discourage writing this in a Python component in Grasshopper. It is just a task for a plug-in, in my opinion (quite too complicated).

Giulio


Giulio Piacentino
for Robert McNeel & Associates
giulio@mcneel.com

2 Likes

@clement
It’s been a while, sorry about the very late reply and thank you alot for your helpful answer! I took your advise on setting a global flag and have atleast started considering undos, new and closed documents etc. There are probably a bunch of things to improve still, but atleast it seems to work out decently by now. Thank you!

@AndersDeleuran
Finally found the time to get this working, atleast on an acceptable level. Code below if you’re interested!

@piac
So far I’m only using the component for experimental purposes, so I don’t think it will cause any harm as of now. I’ll remember your feedback if i’ll release it. Thanks!

if 'events' not in sc.sticky:
    sc.sticky['events'] = {}
sc.sticky['flag'] = False

def subscribe_to(event, func, key):
    if key not in sc.sticky['events']:
        sc.sticky['events'][key] = event
    if key not in sc.sticky:
        sc.sticky[key] = func
        event += sc.sticky[key]

def unsubscribe_all():
    for key in sc.sticky['events']:
        if key in sc.sticky:
            sc.sticky['events'][key] -= sc.sticky[key]
            sc.sticky.Remove(key)
    sc.sticky['events'] = {}

# Set global flag to 'True' when event is fired
def event1(sender, e):
    sc.sticky['flag'] = True

# ExpireSolution if global flag is 'True' and set it back to 'False'
def event2(sender, e):
    if sc.sticky['flag'] == True:
        ghenv.Component.ExpireSolution(True)
        sc.sticky['flag'] = False

def event3(sender, e):
    unsubscribe_all()

subscribe_to(doc.BeforeTransformObjects, event1, 'BeforeTransformObjects')
subscribe_to(Rhino.RhinoDoc.DeleteRhinoObject, event1, 'DeleteRhinoObject')
subscribe_to(Rhino.RhinoApp.Idle, event2, 'Idle')
subscribe_to(Rhino.RhinoDoc.CloseDocument, event3, 'CloseDocument')
subscribe_to(Rhino.RhinoDoc.NewDocument, event3, 'NewDocument')

Any thoughts on this? Thankful for any feedback and suggestions!
Also, i’ve noted that the documentation at: http://developer.rhino3d.com/api/RhinoCommonWin/html/N_Rhino.htm now targets Rhino 6. Do you know where I can find complete documentation for Rhino 5 (latest SR)?

Best
/Jakob

1 Like

They are now located here:

1 Like

Awesome, many thanks Anders!