added
svn: r4279
This commit is contained in:
		
							
								
								
									
										234
									
								
								src/GrampsDBCallback.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								src/GrampsDBCallback.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,234 @@
 | 
			
		||||
#
 | 
			
		||||
# Gramps - a GTK+/GNOME based genealogy program
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2000-2005  Donald N. Allingham
 | 
			
		||||
#
 | 
			
		||||
# This program is free software; you can redistribute it and/or modify
 | 
			
		||||
# it under the terms of the GNU General Public License as published by
 | 
			
		||||
# the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
# (at your option) any later version.
 | 
			
		||||
#
 | 
			
		||||
# This program is distributed in the hope that it will be useful,
 | 
			
		||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
# GNU General Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program; if not, write to the Free Software
 | 
			
		||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# $Id$
 | 
			
		||||
 | 
			
		||||
import types
 | 
			
		||||
 | 
			
		||||
#-------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# Callback signal support for non-gtk parts of Gramps
 | 
			
		||||
#
 | 
			
		||||
#-------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
class GrampsDBCallback(object):
 | 
			
		||||
    """
 | 
			
		||||
    Callback and signal support for non-gtk parts of gramps.
 | 
			
		||||
 | 
			
		||||
    Classes that want to emit signals need to inherit from this
 | 
			
		||||
    class and call its __init__ method. They then need to declare
 | 
			
		||||
    the signals that they can emit and the types of their
 | 
			
		||||
    arguments.
 | 
			
		||||
 | 
			
		||||
    e.g.
 | 
			
		||||
 | 
			
		||||
        class TestSignals(GrampsDBCallback):
 | 
			
		||||
 | 
			
		||||
            __signals__ = {
 | 
			
		||||
                      'test-signal' : (int,)
 | 
			
		||||
                     }
 | 
			
		||||
 | 
			
		||||
            def __init__(self):
 | 
			
		||||
                GrampsDBCallback.__init__(self)
 | 
			
		||||
 | 
			
		||||
            def emit_signal(self):
 | 
			
		||||
                self.emit('test-signal',(1,))
 | 
			
		||||
 | 
			
		||||
    The signals will be inherited by any subclasses.
 | 
			
		||||
 | 
			
		||||
    Attaching a callback to the signals is similar to the gtk
 | 
			
		||||
    connect methods. e.g.
 | 
			
		||||
 | 
			
		||||
        class C(object):
 | 
			
		||||
 | 
			
		||||
            def cb_func(self, i):
 | 
			
		||||
                print "got class signal = ", 1
 | 
			
		||||
 | 
			
		||||
        def fn(i):
 | 
			
		||||
            print "got signal value = ", i
 | 
			
		||||
 | 
			
		||||
        t = TestSignals()
 | 
			
		||||
 | 
			
		||||
        # connect to a function.
 | 
			
		||||
        t.connect('test-signal', fn)
 | 
			
		||||
        
 | 
			
		||||
        t.emit_signal()
 | 
			
		||||
 | 
			
		||||
        r = R()
 | 
			
		||||
 | 
			
		||||
        # connect to a method
 | 
			
		||||
        t.connect_object('test-signal', R.func, r)
 | 
			
		||||
        
 | 
			
		||||
        t.emit_signal()
 | 
			
		||||
    
 | 
			
		||||
    """
 | 
			
		||||
    
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.__callback_map = {}
 | 
			
		||||
        self.__signal_map = {}
 | 
			
		||||
        
 | 
			
		||||
        # Build signal list, the signals can't change so we only
 | 
			
		||||
        # need to do this once.            
 | 
			
		||||
        def trav(cls):
 | 
			
		||||
            if cls.__dict__.has_key('__signals__'):
 | 
			
		||||
                signal_list = [cls.__signals__]
 | 
			
		||||
            else:
 | 
			
		||||
                signal_list = []
 | 
			
		||||
 | 
			
		||||
            for base_cls in cls.__bases__:
 | 
			
		||||
                base_list = trav(base_cls)
 | 
			
		||||
                if len(base_list) > 0:
 | 
			
		||||
                    signal_list = signal_list + base_list
 | 
			
		||||
 | 
			
		||||
            return signal_list
 | 
			
		||||
 | 
			
		||||
        # Build a signal dict from the list of signal dicts
 | 
			
		||||
        for s in trav(self.__class__):
 | 
			
		||||
            for (k,v) in s.items():
 | 
			
		||||
                self.__signal_map[k] = v
 | 
			
		||||
 | 
			
		||||
        # self.__signal_map now contains the connonical list
 | 
			
		||||
        # of signals that this instance can emit.
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
    def connect(self, signal_name, callback):
 | 
			
		||||
        # Check that signal exists.
 | 
			
		||||
        if signal_name not in self.__signal_map.keys():
 | 
			
		||||
            print "Warning: attempt to connect to unknown signal: ", str(signal_name)
 | 
			
		||||
            return
 | 
			
		||||
        
 | 
			
		||||
        # Add callable to callback_map
 | 
			
		||||
        if signal_name not in self.__callback_map.keys():
 | 
			
		||||
            self.__callback_map[signal_name] = []
 | 
			
		||||
        self.__callback_map[signal_name].append(callback)
 | 
			
		||||
 | 
			
		||||
    def connect_object(self, signal_name, class_method, instance):
 | 
			
		||||
        # Check that signal exists.
 | 
			
		||||
        if signal_name not in self.__signal_map.keys():
 | 
			
		||||
            print "Warning: attempt to connect to unknown signal: ", str(signal_name)
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        # Add object and instance to callback_map
 | 
			
		||||
        self.__callback_map[signal_name].append((class_method,instance))
 | 
			
		||||
 | 
			
		||||
    def emit(self, signal_name, args=tuple()):
 | 
			
		||||
        # Check signal exists
 | 
			
		||||
        if signal_name not in self.__signal_map.keys():
 | 
			
		||||
            print "Warning: attempt to emit to unknown signal: ", str(signal_name)
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        # type check arguments
 | 
			
		||||
        arg_types = self.__signal_map[signal_name]
 | 
			
		||||
        if arg_types == None and len(args) > 0:
 | 
			
		||||
            print "Warning: signal emitted with wrong number of args: ", str(signal_name)
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if len(args) > 0:
 | 
			
		||||
            if len(args) != len(arg_types):
 | 
			
		||||
                print "Warning: signal emitted with wrong number of args: ", str(signal_name)
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
            if arg_types != None:
 | 
			
		||||
                for i in range(0,len(arg_types)):
 | 
			
		||||
                    if type(args[i]) != arg_types[i]:
 | 
			
		||||
                        print "Warning: signal emitted with wrong arg types: ", str(signal_name)
 | 
			
		||||
                        return 
 | 
			
		||||
                
 | 
			
		||||
        for cb in self.__callback_map[signal_name]:
 | 
			
		||||
            try:
 | 
			
		||||
                if type(cb) == tuple: # call class method
 | 
			
		||||
                    cb[0](cb[1],*args)
 | 
			
		||||
                elif type(cb) == types.FunctionType or \
 | 
			
		||||
                         type(cb) == types.MethodType: # call func
 | 
			
		||||
                    cb(*args)
 | 
			
		||||
                else:
 | 
			
		||||
                    print "Warning: badly formed entry in callback map"
 | 
			
		||||
            except:
 | 
			
		||||
                print "Warning: exception occured in callback function."
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#-------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# Testing code below this point
 | 
			
		||||
#
 | 
			
		||||
#-------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class TestSignals(GrampsDBCallback):
 | 
			
		||||
 | 
			
		||||
        __signals__ = {
 | 
			
		||||
                  'test-signal' : (int,)
 | 
			
		||||
                 }
 | 
			
		||||
 | 
			
		||||
        def __init__(self):
 | 
			
		||||
            GrampsDBCallback.__init__(self)
 | 
			
		||||
 | 
			
		||||
        def emit_signal(self):
 | 
			
		||||
            self.emit('test-signal',(1,))
 | 
			
		||||
            self.emit('test-signal',(0,2))
 | 
			
		||||
            self.emit('test-signal',(2.0,))
 | 
			
		||||
 | 
			
		||||
    class TestSignalsSubclass(TestSignals):
 | 
			
		||||
 | 
			
		||||
        __signals__ = {
 | 
			
		||||
                  'test-base-signal' : (str,str,list),
 | 
			
		||||
                  'test-noargs'      : None
 | 
			
		||||
                 }
 | 
			
		||||
 | 
			
		||||
        def emit_signal(self):
 | 
			
		||||
            self.emit('test-signal',(1,))
 | 
			
		||||
            self.emit('test-base-signal',("Hi","There",[1,2,3]))
 | 
			
		||||
            self.emit('test-noargs')
 | 
			
		||||
 | 
			
		||||
    class R(object):
 | 
			
		||||
 | 
			
		||||
        def func(self, i):
 | 
			
		||||
            print "got class signal = ", 1
 | 
			
		||||
 | 
			
		||||
    def fn(i):
 | 
			
		||||
        print "got signal value = ", i
 | 
			
		||||
 | 
			
		||||
    def fn2(s,t,l):
 | 
			
		||||
        print "got signal value = ", s, t, repr(l)
 | 
			
		||||
 | 
			
		||||
    def fn3():
 | 
			
		||||
        print "got signal noargs"
 | 
			
		||||
 | 
			
		||||
    t = TestSignals()
 | 
			
		||||
    t.connect('test-signal', fn)
 | 
			
		||||
    t.connect('test-signal', fn)
 | 
			
		||||
    t.connect('test-signal', fn)
 | 
			
		||||
    t.connect('test-signal', fn)
 | 
			
		||||
    #t.emit_signal()
 | 
			
		||||
 | 
			
		||||
    r = R()
 | 
			
		||||
    t.connect_object('test-signal', R.func, r)
 | 
			
		||||
    t.connect('test-signal',r.func)
 | 
			
		||||
    
 | 
			
		||||
    t.emit_signal()
 | 
			
		||||
 | 
			
		||||
    s = TestSignalsSubclass()
 | 
			
		||||
    s.connect('test-signal', fn)
 | 
			
		||||
    s.connect('test-base-signal', fn2)
 | 
			
		||||
    s.connect('test-noargs', fn3)
 | 
			
		||||
    #s.emit_signal()
 | 
			
		||||
		Reference in New Issue
	
	Block a user