Welcome, guest | Sign In | My Account | Store | Cart

A module that allows you to not to repeat yourself (DRY) while writing typical PyGTK constructions (an object containing PyGTK widgets with its methods servicing widget events) by calling connect() automatically. See docstring.

Python, 96 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#!/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))

2 comments

James Mills 13 years, 1 month ago  # | flag

I tried to do something very similar for the circuits application framework where there is a GtkDriver which ticks over the Gtk sub-system while allowing the rest of circuits to continue running other things and maintain events.

I never got around to hooking the Gtk Events into circuits though and making this nice.

Maybe I should re-look at this.

Nice recipe though :)

--JamesMills (prologic)

Pavel Krc (author) 13 years, 1 month ago  # | flag

Instead of a single underscore, one could use two underscores as a separator between member and event name. It would certainly decrease the risk of ambiguities, and it is up to you to decide whether my_window__delete_event looks more intuitive than my_window_delete_event.

Created by Pavel Krc on Thu, 17 Feb 2011 (MIT)
Python recipes (4591)
Pavel Krc's recipes (2)

Required Modules

Other Information and Tasks