#!/usr/bin/env python # -*- coding: utf-8 -*- '''Connecting PyGTK object events to class methods automatically. Helps you not to repeat yourself (DRY) with PyGTK. Example: from pygtkconnect import * class MyClass: (...) @gtk_event def my_button_clicked(self, widget, data=None): (...) def __init__(self): (...) gtk_insert(self, 'my_button', gtk.Button('click me')) # also does self.my_button = ... (...) gtk_connect_events(self) # calls self.my_button.connect('clicked', self.my_button_clicked) # and similar for all decorated methods in one line of code :) If you call gtk_connect_events(self, False), you can even skip the @gtk_event decorators, but then you have to be careful with method names! ''' __author__ = 'Pavel Krc' __email__ = 'pk-alt@seznam.cz' __date__ ='2011-02-17' __all__ = ['gtk_event', 'gtk_insert', 'gtk_connect_events', 'PygtkconnectException'] import re class PygtkconnectException(Exception): pass def gtk_event(method): '''A decorator that makes a method connectible to an event The method name must be exactly [_[_]]membername_eventname. ''' method._pygtkconnect_method_is_event = True return method def gtk_insert(user_object, member_name, gtk_object, insert_as_member=True): '''Enables PyGTK event binding for a PyGTK object You must make sure that the member names are not ambiguous, i.e. if you remove leading underscores from member names, then a member name with added underscore at the end isn't a prefix of some other member name. "member" and "member1" are OK, "__member" and "member_two" are not. If insert_as_member is True, the gtk object is also inserted as a member to user_object (using member_name). ''' if not hasattr(user_object, '__pygtkconnect_members'): user_object.__pygtkconnect_members = {member_name: gtk_object} else: user_object.__pygtkconnect_members[member_name] = gtk_object if insert_as_member: setattr(user_object, member_name, gtk_object) def gtk_connect_events(user_object, require_decorators=True): '''Connects decorated event methods as event handlers to selected members If require_decorators is False, the methods used as events are determined purely by name prefix (be careful!). ''' members = user_object.__pygtkconnect_members if require_decorators: funcs = [name for name, obj in user_object.__class__.__dict__.iteritems() if hasattr(obj, '_pygtkconnect_method_is_event')] else: funcs = [name for name, obj in user_object.__class__.__dict__.iteritems() if callable(obj)] # create a regex with member names for separating member name and event names_reg = re.compile('^(_{0,2})(%s)_(.*)$' % '|'.join(members.iterkeys())) # connect it all for funcname in funcs: m = names_reg.match(funcname) if not m: if require_decorators: raise PygtkconnectException('Invalid method name or no matched ' 'member object!') else: continue leading_uscores, member_name, event_name = m.groups() members[member_name].connect(event_name, getattr(user_object, funcname))