Python eto custom event

Does anyone know of a way to make custom events with python and eto? Is there an eto-specific way to do this? I’ve not been able to find any examples or ‘hints’. I’ve found an ironpython book that seems to discuss this, but it is from 2010 dealing with windows forms, and I’m not sure it is applicable to eto.


Hi @nathancoatney,

What do you mean by custom event? What are you trying to do and why?


– Dale

Hello Dale,

For example:

I would like to make a reusable panel containing a text box in which the user can specify comma separated rotations such as 0, 45, 90, 270-360 (like print options where you can specify specific pages or range of pages). Making the text box and parsing the text on .TextChanged or .TextChanging events is straight forward, the handler splits and cleans up the input the way I want it. What I would like to do is make an event that emits from the reusable panel when the handler is successful and has valid output, not on every change that is made to the text.

So I would have a main form, which contains instances of my reusable panel (one for x, y and z axis). Each instance would have an event called something like rotation_input_valid which I could handle in the main form to use elsewhere.

I guess I’m trying to make self contained panels of controls that gather user input, process it, and produce an output, without having to expose all the inner workings of the panel.

Is that a clear description? It seems common to make custom events in c#, but I haven’t been able to translate that to ironpython (and maybe eto is another layer of confusion).

Hi @nathancoatney,

You might take a look at this code:

Here is an example of its usage.

– Dale

Thanks for the hints. I had seen pyevent previously but didn’t understand/grasp any examples. After some experiments It seems to be working. I copied the to the scripts folder for import. Here is what is working here, in case anyone else is interested:

import System
import Rhino.UI
import Eto.Drawing as drawing
import Eto.Forms as forms

import pyevent  # copied to rhino scripts folder for import

class EventArgs:
    """A container object for the event arguments."""
    def __init__(self):
        self.default_arg = 'default arg'

class ButtonPanel(forms.Panel):
    """Self contained form with button and custom event."""
    # Create custom events out here.
    custom_button_event, _custom_button_event_emmiter = pyevent.make_event()
    def __init__(self):
        # layout
        self.layout = forms.DynamicLayout()
        self.layout.DefaultSpacing = drawing.Size(5, 5)
        self.layout.Padding = drawing.Padding(10)
        # button
        self.event_button = forms.Button()
        self.event_button.Text = 'Test Event'
        # Hook the button click event to our logic.
        self.event_button.Click += self.button_clean_up_function
        self.Content = self.layout
    def button_clean_up_function(self, sender, e):
        # This is where we intercept the button click.
        # Make our own event argument object.
        event_args = EventArgs()
        # Can just add things like this:
        event_args.complicated_data = 'complicated'
        # Now we can call our custom event emitter.
        # I think the emitter will pass along anything we pass to it.
        # This example we pass self and our event_args, but I think it can be anything.
        # It seems we can use self. or ButtonPanel. - not sure what implications are.
        # self. makes more sense to me, so I'll go with that one.
        self._custom_button_event_emmiter(self, event_args)

class InfoPanel(forms.Panel):
    """Self contained info panel"""
    def __init__(self):
        # layout
        self.layout = forms.DynamicLayout()
        self.layout.DefaultSpacing = drawing.Size(5, 5)
        self.layout.Padding = drawing.Padding(10)
        # label
        self.info_label = forms.Label()
        self.info_label.Text = 'No event yet'
        self.Content = self.layout
    # This will be attached to our custom event, so args need to match
    def update_label(self, sender, e):
        # make a string to display in the label
        # shows the sender object, the EventArgs object, and our data from the EventArgs object
        info = 'sender: {}\n' \
               'e: {}\n' \
               'e.complicated_data: {}'.format(sender, e, e.complicated_data)
        self.info_label.Text = info

class CustomEventTestDialog(forms.Dialog):
    def __init__(self):
        self.Title = 'Custom Event Test'
        self.Padding = drawing.Padding(10)
        self.Resizable = True
        self.MinimumSize = drawing.Size(600, 200)
    def create(self):
        # make the button panel instance
        self.button_panel = ButtonPanel()
        # make the info panel instance
        self.info_panel = InfoPanel()
        # hook up the custom event
        self.button_panel.custom_button_event += self.info_panel.update_label
        # layout
        self.layout = forms.DynamicLayout()
        self.Content = self.layout
def custom_event_test_dialog():
    dialog = CustomEventTestDialog()
    except Exception as exp:
        print('Could not load dialog:')
if __name__ == '__main__':

Found a gotcha:

Code above makes the events class methods (I think). If you make multiple instances then they all share the event.

To make the events isolated to each instance put the in the init method and a self. on the front of them, like normal. So:

class MyClass:
    def __init__(self):
        self.custom_button_event, self._custom_button_event_emitter = pyevent.make_event()

So far no problems with this.