Skip to content

Firing Enaml events from traits events #127

corranwebster opened this Issue Mar 28, 2012 · 9 comments

3 participants

Enthought, Inc. member

Currently there is no way to cause a change to occur in an Enaml UI based on a trait event firing. The obvious pattern of:

attr foo
attr foo_event << foo.event

doesn't work because traits events are write-only, and << tries to read the value.

A solution would be to allow Enaml events to fire when they are set, as well as when they are called, so that:

attr foo
event foo_event << foo.event

would cause foo_event to fire with the value that was written to foo.event.


Fixed in PR #128

@sccolbert sccolbert closed this Mar 28, 2012
Enthought, Inc. member

As we discussed yesterday, #128 doesn't completely solve the issue since there is still no way to bind a traits event firing to an Enaml event, although the suggestion in the original feature request is probably not the right syntax.

What we need is a way to map the firing of a trait event (or, more generally a write-only trait) to the firing of an Enaml event without having to go through some sort of intermediate model-view. There may be a number of different approaches to solving this which can be discussed further, but at the root there may be situations where the underlying notification system that Enaml is sitting on top of has events which fire with state, but the event state is ephemeral and not bound into any particular attribute of the model.

Trait events are a standard part of traits, and we need to provide some sort of support for them for at least the near-term.

@corranwebster corranwebster reopened this Mar 29, 2012

I found a short-term workaround for this.
In enaml:

attr foo
event foo_event << foo.proxy

And in python:

myevent = Event
proxy = Bool

def _myevent_changed(self):
    self.proxy = not self.proxy

This is something that would be far better served using a controller to perform whatever you are doing in your handler for foe_event ::. i.e. i'm still not sold on the need to shove model events into a view event.

IMHO if a model has an event defined, the handler for that event should be on the model or a controller, not the view. I'll need to make some examples of adding a controller to an Enaml application.


My intent was to fire a context menu for a chaco plot using normal_right_down. Do you know of a cleaner way to do this?


Do you have a small code snippet that shows what you are trying to do?


In python:

class MyPlot(Plot):
    popup = Bool

    def normal_right_down(self, event):
        self.popup = not self.popup

In enaml:

enamldef PlotForm(Form):
    attr plot
    event popup_event << plot.popup
    popup_event ::  pmenu.popup()
        id : pmenu

By the way enaml is awesome. I started using it in earnest just this afternoon and I already have a dockable chaco plot with a context menu working. Mind boggling.


You shouldn't define the Menu as a child of your Form. While it may work, it's conceptually not appropriate. It's better to define it separately, and pass the parent to the .popup method.

As I mentioned before, this kind of thing is better captured with a controller pattern IMHO. Something like this should work (pseudo'ish code):

# my_plot.enaml

enamldef PlotWin(Window):
        name = 'plot_form'

enamldef PlotMenu(Menu):


class MyPlot(Plot):
    popup = Event
    def normal_right_down(self, event):
        self.popup = True

class Controller(HasTraits):
    menu_cls = Any
    plot_form = Any
    plot_model = Instance(MyPlot)

    def show_popup_menu(self):
        menu = self.menu_cls()

with enaml.imports():
    from my_plot import PlotWindow, PlotMenu

model = MyPlot()
view = PlotWindow(plot_model=model)
plot_form = view.find_by_name('plot_form')
controller = Controller(menu_cls=PlotMenu, plot_form=plot_form, plot_model=model)


Worked like a charm with one change:

@on_trait_change('plot_model:popup')   # was 'plot_form:popup'
def show_popup_menu(self):


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.