Skip to content
This repository

Firing Enaml events from traits events #127

Open
corranwebster opened this Issue · 9 comments

3 participants

Corran Webster S. Chris Colbert Steven Silvester
Corran Webster
Collaborator

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.

S. Chris Colbert

Fixed in PR #128

Corran Webster
Collaborator

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.

Steven Silvester

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
S. Chris Colbert

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.

Steven Silvester

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?

S. Chris Colbert

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

Steven Silvester

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()
    Menu:
        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.

S. Chris Colbert

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):
    Form:
        name = 'plot_form'

enamldef PlotMenu(Menu):
    pass

# my_plot_model.py

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)

    @on_trait_change('plot_form:popup')
    def show_popup_menu(self):
        menu = self.menu_cls()
        menu.popup(parent=self.plot_form)


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)

view.show()

Steven Silvester

Worked like a charm with one change:

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

Thanks!

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.