Using a metaclass to avoid having to call `self.Bind`

Hello folks,

It annoys me to have all these self.Bind calls in my widgets. I’m thinking of writing a metaclass that detects methods that have names that look like event handlers and binding them automatically. It’s still in the planning phase but I would say with 75% certainty that it’s possible to make such a metaclass that would work well enough.

My question: Is there any existing code that does something like this?

Ram.

Similar things have been tried in the past (but not with meta-classes as far as I know) but they have usually failed to catch on, and in at least one case caused more bugs than the convenience warranted.

One use-case that has been a stumbling block in the past is catching events originating from child or grandchild widgets. For example if you have a frame with a panel and some buttons on the panel. You probably do not want to require that every button be subclassed in order to give them an OnButton method that is automatically bound, most people would rather handle the button events in the frame or panel class. You could instead use the widget names as part of the method name to look for:

  foobtn = wx.Button(self, -1, 'clicker', name='Foo')
    barbtn = wxButton(self, -1, 'clacker', name='Bar')

  ...

  def on_Foo_button(self, evt):
    ...
  def on_Bar_button(self, evt):
    ...

but that tends to get awkward sometimes as well.

···

On 6/1/11 4:17 PM, cool-RR wrote:

Hello folks,

It annoys me to have all these `self.Bind` calls in my widgets. I'm
thinking of writing a metaclass that detects methods that have names
that look like event handlers and binding them automatically. It's still
in the planning phase but I would say with 75% certainty that it's
possible to make such a metaclass that would work well enough.

My question: Is there any existing code that does something like this?

--
Robin Dunn
Software Craftsman

cool-RR wrote:

Hello folks,

It annoys me to have all these `self.Bind` calls in my widgets. I'm
thinking of writing a metaclass that detects methods that have names
that look like event handlers and binding them automatically. It's
still in the planning phase but I would say with 75% certainty that
it's possible to make such a metaclass that would work well enough.

My question: Is there any existing code that does something like this?

There are some tricky aspects to this. In the case of button events,
how will you know which button to catch?

One relatively easy way to do this is the MFC approach: write a mix-in
class that binds to (almost) every possible event and calls a function
with a standard name that does nothing but call Skip. Then, in your
derived classes, you just implement the events you really want.

···

--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

I thought about the child/grandchild thing, I don’t see what’s the big deal. I already implemented the child thing, and if I’ll continue with this I’ll implement the grandchild thing too. In my system you just do def _on_ok_button and then it’s bound to wx.EVT_BUTTON from self.ok_button. (This should answer your question as well Tim.) That’s it. If I’ll want grandchild support I could just parse def _on_child_grandchild. And if the event is not clear from the widget type I could let them add the event name at the end of the method.

Ram.

···

On Thu, Jun 2, 2011 at 6:37 PM, Robin Dunn robin@alldunn.com wrote:

On 6/1/11 4:17 PM, cool-RR wrote:

Hello folks,

It annoys me to have all these self.Bind calls in my widgets. I’m

thinking of writing a metaclass that detects methods that have names

that look like event handlers and binding them automatically. It’s still

in the planning phase but I would say with 75% certainty that it’s

possible to make such a metaclass that would work well enough.

My question: Is there any existing code that does something like this?

Similar things have been tried in the past (but not with meta-classes as far as I know) but they have usually failed to catch on, and in at least one case caused more bugs than the convenience warranted.

One use-case that has been a stumbling block in the past is catching events originating from child or grandchild widgets. For example if you have a frame with a panel and some buttons on the panel. You probably do not want to require that every button be subclassed in order to give them an OnButton method that is automatically bound, most people would rather handle the button events in the frame or panel class. You could instead use the widget names as part of the method name to look for:

    foobtn = wx.Button(self, -1, 'clicker', name='Foo')

    barbtn = wxButton(self, -1, 'clacker', name='Bar')



    ...



    def on_Foo_button(self, evt):

            ...

    def on_Bar_button(self, evt):

            ...

but that tends to get awkward sometimes as well.

Robin Dunn

I just want to say that it makes me very uneasy whenever someone’s writing code that does things like examine dict, take actions based on variable names, and so on. It breaks a number of assumptions that should be safe to make, for example that if you change a variable name everywhere that variable is referenced, then the behavior of the code will not change. It also tends to result is opaque, hard-to-debug code. Having dealt with this kind of thing in the past, it just really doesn’t seem to be worth the effort.

Obviously you’re free to write whatever you like. Just keep in mind that other people (like yourself a few months down the line) will have to maintain this code, and they may well not like what you’ve done. “Magic” in programming is generally not desirable, especially if it comes at the cost of transparency.

-Chris

···

On Thu, Jun 2, 2011 at 12:36 PM, cool-RR cool-rr@cool-rr.com wrote:

I thought about the child/grandchild thing, I don’t see what’s the big deal. I already implemented the child thing, and if I’ll continue with this I’ll implement the grandchild thing too. In my system you just do def _on_ok_button and then it’s bound to wx.EVT_BUTTON from self.ok_button. (This should answer your question as well Tim.) That’s it. If I’ll want grandchild support I could just parse def _on_child_grandchild. And if the event is not clear from the widget type I could let them add the event name at the end of the method.

I don’t see how this is different from any other case in programming when you have to give your object a specific name in order for it to be used. For example, you want a constructor? You have to call it __init__. You want something to happen before your dialog gets show-modalled? You have to name your new method ShowModal and do your action there. You want to define urls for your Django project? You have to call your list urls, etc. You can’t rename them, you must use the blessed names and you have no choice. I don’t see how my idea is less legitimate than these examples.

Ram.

···

On Thu, Jun 2, 2011 at 9:42 PM, Chris Weisiger cweisiger@msg.ucsf.edu wrote:

On Thu, Jun 2, 2011 at 12:36 PM, cool-RR cool-rr@cool-rr.com wrote:

I thought about the child/grandchild thing, I don’t see what’s the big deal. I already implemented the child thing, and if I’ll continue with this I’ll implement the grandchild thing too. In my system you just do def _on_ok_button and then it’s bound to wx.EVT_BUTTON from self.ok_button. (This should answer your question as well Tim.) That’s it. If I’ll want grandchild support I could just parse def _on_child_grandchild. And if the event is not clear from the widget type I could let them add the event name at the end of the method.

I just want to say that it makes me very uneasy whenever someone’s writing code that does things like examine dict, take actions based on variable names, and so on. It breaks a number of assumptions that should be safe to make, for example that if you change a variable name everywhere that variable is referenced, then the behavior of the code will not change. It also tends to result is opaque, hard-to-debug code. Having dealt with this kind of thing in the past, it just really doesn’t seem to be worth the effort.

Obviously you’re free to write whatever you like. Just keep in mind that other people (like yourself a few months down the line) will have to maintain this code, and they may well not like what you’ve done. “Magic” in programming is generally not desirable, especially if it comes at the cost of transparency.

-Chris

It’s mostly a matter of expectations and education. It’s well-documented in Python that the constructor is always named init, there’s a comparator you can implement named cmp, you can cast to string with repr and str, etc. Similarly, WX defines a specific set of classes with specific functions that you can override. I’m not familiar with Django but I’m sure a similar principle applies. None of these are surprising, so they don’t bother people. However, if you found, say, as C++ program that overrode the “+” operator to print out Pascal’s Triangle instead of adding values together, I bet you’d be surprised.

This doesn’t apply to your one-off solution, simply because it is a one-off solution. Anything it does differently from the way things are usually done in its context (in this case, Python and WX) is surprising. If it stops being a one-off solution and instead becomes a tool in its own right that users can be reasonably expected to read up on before they start mucking with your code, then you no longer have this problem. If you have aims in that direction, well, all I’ll say is that in the meantime please, please comment copiously. :slight_smile:

-Chris

···

On Thu, Jun 2, 2011 at 12:48 PM, cool-RR cool-rr@cool-rr.com wrote:

I don’t see how this is different from any other case in programming when you have to give your object a specific name in order for it to be used. For example, you want a constructor? You have to call it __init__. You want something to happen before your dialog gets show-modalled? You have to name your new method ShowModal and do your action there. You want to define urls for your Django project? You have to call your list urls, etc. You can’t rename them, you must use the blessed names and you have no choice. I don’t see how my idea is less legitimate than these examples.

This is not intended as a one-off solution. It’s intended as a tool, as you say, and of course it will be documented like all the code that I release. If this works well I intend to use it on the dozens of widgets in my program, and it will all be released as LGPL. I’ll send you an email when I make a release, probably 2 months from now.

Ram.

···

On Thu, Jun 2, 2011 at 10:05 PM, Chris Weisiger cweisiger@msg.ucsf.edu wrote:

On Thu, Jun 2, 2011 at 12:48 PM, cool-RR cool-rr@cool-rr.com wrote:

I don’t see how this is different from any other case in programming when you have to give your object a specific name in order for it to be used. For example, you want a constructor? You have to call it __init__. You want something to happen before your dialog gets show-modalled? You have to name your new method ShowModal and do your action there. You want to define urls for your Django project? You have to call your list urls, etc. You can’t rename them, you must use the blessed names and you have no choice. I don’t see how my idea is less legitimate than these examples.

It’s mostly a matter of expectations and education. It’s well-documented in Python that the constructor is always named init, there’s a comparator you can implement named cmp, you can cast to string with repr and str, etc. Similarly, WX defines a specific set of classes with specific functions that you can override. I’m not familiar with Django but I’m sure a similar principle applies. None of these are surprising, so they don’t bother people. However, if you found, say, as C++ program that overrode the “+” operator to print out Pascal’s Triangle instead of adding values together, I bet you’d be surprised.

This doesn’t apply to your one-off solution, simply because it is a one-off solution. Anything it does differently from the way things are usually done in its context (in this case, Python and WX) is surprising. If it stops being a one-off solution and instead becomes a tool in its own right that users can be reasonably expected to read up on before they start mucking with your code, then you no longer have this problem. If you have aims in that direction, well, all I’ll say is that in the meantime please, please comment copiously. :slight_smile:

-Chris

cool-RR wrote:

I don't see how this is different from any other case in programming
when you have to give your object a specific name in order for it to
be used. For example, you want a constructor? You have to call it
`__init__`. You want something to happen before your dialog gets
show-modalled? You have to name your new method `ShowModal` and do
your action there.

Well, those cases ARE different. Those are fixed, well-publicized, and
well-known names. You are talking about inventing new names based on a
set of heuristics. I need to understand those heuristics completely in
order to predict what name I should be using.

I can call my version of "ShowModal" anything I want, like
"BlastItUpThere". That will still work, as long as I call it by that
name. That's all under my control. You're hooking up names without my
involvement. Not good or bad, but different cases.

In my system you just do `def _on_ok_button` and then it's bound to
`wx.EVT_BUTTON` from `self.ok_button`. (This should answer your
question as well Tim.)

Are you planning to generate the Bind calls based on the functions you
find, or you planning to search for controls and then generate the
function names? Remember there are other events -- I might want to have
focus in and focus out handlers for a button, as well as a "clicked"
handler. In many cases, the callback function name will need to include
both the event and the control name (_on_button_ok_button,
_on_set_focus_ok_button, _on_kill_focus_ok_button). MFC and VB both do
things this way.

This requires that all of my controls be instance members, as opposed to
locals. This also requires that I adopt to your naming scheme (that is,
"on_ok_button" as opposed to "onOkButton").

I don't see how my idea is less legitimate than these examples.

I'm not sure anyone ever called it illegitimate. However, like Chris,
my experience tells me that the corner conditions and special cases mean
that you will end up writing more code with your scheme than you would
have calling self.Bind. I think it would be interesting for you to take
a whack at it and see what happens, however. It's an interesting concept.

···

--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

And the OP originally asked if similar efforts had been tried before. I don't know how similar they are, but I'd take a look for:

Wax
PythonCard
Dabo

I don't know what has happened to Wax and PythonCard, but Dabo is alive and well, and working on a somewhat cleaner and easier API over wxPython.

-Chris

···

On 6/2/11 2:23 PM, Tim Roberts wrote:

I think it would be interesting for you to take
a whack at it and see what happens, however. It's an interesting concept.

--
Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Dabo indeed implements this:

btn = dabo.ui.dButton(self, Caption="Click Me", OnHit=self.buttonHandler)

Any keyword param that is "On" + the name of an event is auto-bound to
that event. In Dabo, the 'Hit' event is raised when the user clicks a
button, so in this code, the button click would be bound to
'self.buttonHandler'.

···

On Fri, Jun 3, 2011 at 1:04 AM, Chris Barker <Chris.Barker@noaa.gov> wrote:

On 6/2/11 2:23 PM, Tim Roberts wrote:

I think it would be interesting for you to take
a whack at it and see what happens, however. It's an interesting concept.

And the OP originally asked if similar efforts had been tried before. I
don't know how similar they are, but I'd take a look for:

Wax
PythonCard
Dabo

I don't know what has happened to Wax and PythonCard, but Dabo is alive and
well, and working on a somewhat cleaner and easier API over wxPython.

--

# p.d.

Peter Decker wrote:

Dabo indeed implements this:

btn = dabo.ui.dButton(self, Caption="Click Me", OnHit=self.buttonHandler)

Any keyword param that is "On" + the name of an event is auto-bound to
that event. In Dabo, the 'Hit' event is raised when the user clicks a
button, so in this code, the button click would be bound to
'self.buttonHandler'.

This is a little different - it looks like a way to specify Bindings in the constructor, rather than a separate Bind() call -- which I do like -- seems more Pythoninc to me.

But I think the OP was thinking about an approach that didn't require an specification of the Binding at all -- simply using following the right pattern for the name of the handler.

-Chris

···

--
Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

In a moment of inspiration I wrote a simple solution as a middle ground. See attachment.

You still have to be explicit (which is a good thing according to the Zen) but you might like this syntax better.

Cheers

autobind.py (1.39 KB)

···

On Thu, 02 Jun 2011 01:17:04 +0200, cool-RR <ram.rachum@gmail.com> wrote:

Hello folks,

It annoys me to have all these `self.Bind` calls in my widgets. I'm thinking
of writing a metaclass that detects methods that have names that look like
event handlers and binding them automatically. It's still in the planning
phase but I would say with 75% certainty that it's possible to make such a
metaclass that would work well enough.

My question: Is there any existing code that does something like this?

Ram.

Ah, I misread. Well, in that case, Dabo can still take care of that.
If you have the setting for 'autoBindEvents' set to True (the default)
any method of a class that follows the pattern 'on<EventName>' will
automatically bind that event to that method. In the example above,
instead of passing the binding in the constructor, you would define a
button class with an 'onHit(self, evt):' method, and it will auto-bind
the Hit event to that method.

Is that close to what was desired?

···

On Fri, Jun 3, 2011 at 2:31 PM, Christopher Barker <Chris.Barker@noaa.gov> wrote:

Peter Decker wrote:

Dabo indeed implements this:

btn = dabo.ui.dButton(self, Caption="Click Me", OnHit=self.buttonHandler)

Any keyword param that is "On" + the name of an event is auto-bound to
that event. In Dabo, the 'Hit' event is raised when the user clicks a
button, so in this code, the button click would be bound to
'self.buttonHandler'.

This is a little different - it looks like a way to specify Bindings in the
constructor, rather than a separate Bind() call -- which I do like -- seems
more Pythoninc to me.

But I think the OP was thinking about an approach that didn't require an
specification of the Binding at all -- simply using following the right
pattern for the name of the handler.

--

# p.d.

Toni Ruža, the code in autobind.py is just an awful mess. And cool-RR, just use the Bind method normally as we all do. Why don’t you like the OOP approach?

Boštjan Mejak wrote:

Toni Ruža, the code in autobind.py is just an awful mess.

Why do you think so? Not everybody agrees with the decorator approach
to extending syntax, but his code has several very positive attributes:
1. It is very compact
2. It will work with arbitrary events
3. It doesn't require any custom naming schemes

It is better than the mix-in approach I suggested.

···

--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

Why can’t you just bind events the old fashion way by using the method Bind on an object? Why do you need to invent some junk just so you get automatic binding behaviour? If Robin Dunn had seen this as a benefit, he would probably incorporate the automatic widget binding behaviour in wxPython by now.

So you are saying that we should never try to improve the way things are done? Trust tradition and consider innovation attempts sacrilege?

I'm with you on the old fashion Bind, I never had a problem with it. But I won't automatically shoot down any attempts to improve it, however unlikely the success and especially if it presents an opportunity for some creative thinking.

···

On Sat, 04 Jun 2011 03:27:38 +0200, Boštjan Mejak <bostjan.mejak@gmail.com> wrote:

Why can't you just bind events the old fashion way by using the method Bind
on an object? Why do you need to invent some junk just so you get automatic
binding behaviour? If Robin Dunn had seen this as a benefit, he would
probably incorporate the automatic widget binding behaviour in wxPython by
now.

I simplified my solution a bit by getting rid of the __getattribute__ magic. By doing this I don't have to handle a event namespace parameter for events that are not in the wx namespace and autocompletion in editors works.

Also, it's a bit more feature complete. Event handler name is optional and it supports ids. The example showcases these changes.

I made a project for it on bitbucket.
http://bitbucket.org/raz/wxeventbinder

···

On Fri, 03 Jun 2011 21:42:42 +0200, Toni Ruža <gmr.gaf@gmail.com> wrote:

In a moment of inspiration I wrote a simple solution as a middle ground. See attachment.

You still have to be explicit (which is a good thing according to the Zen) but you might like this syntax better.

BTW, there are things like this in the wx.lib (evtmgr, eventwatcher, eventStack) and they are about a decade old.

···

On Sat, 04 Jun 2011 03:27:38 +0200, Boštjan Mejak <bostjan.mejak@gmail.com> wrote:

If Robin Dunn had seen this as a benefit, he would
probably incorporate the automatic widget binding behaviour in wxPython by
now.