| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  | """
 | 
					
						
							|  |  |  | MultiCall - a class which inherits its methods from a Tkinter widget (Text, for | 
					
						
							|  |  |  | example), but enables multiple calls of functions per virtual event - all | 
					
						
							|  |  |  | matching events will be called, not only the most specific one. This is done | 
					
						
							|  |  |  | by wrapping the event functions - event_add, event_delete and event_info. | 
					
						
							|  |  |  | MultiCall recognizes only a subset of legal event sequences. Sequences which | 
					
						
							|  |  |  | are not recognized are treated by the original Tk handling mechanism. A | 
					
						
							|  |  |  | more-specific event will be called before a less-specific event. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The recognized sequences are complete one-event sequences (no emacs-style | 
					
						
							|  |  |  | Ctrl-X Ctrl-C, no shortcuts like <3>), for all types of events. | 
					
						
							|  |  |  | Key/Button Press/Release events can have modifiers. | 
					
						
							|  |  |  | The recognized modifiers are Shift, Control, Option and Command for Mac, and | 
					
						
							|  |  |  | Control, Alt, Shift, Meta/M for other platforms. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | For all events which were handled by MultiCall, a new member is added to the | 
					
						
							|  |  |  | event instance passed to the binded functions - mc_type. This is one of the | 
					
						
							|  |  |  | event type constants defined in this module (such as MC_KEYPRESS). | 
					
						
							|  |  |  | For Key/Button events (which are handled by MultiCall and may receive | 
					
						
							|  |  |  | modifiers), another member is added - mc_state. This member gives the state | 
					
						
							|  |  |  | of the recognized modifiers, as a combination of the modifier constants | 
					
						
							|  |  |  | also defined in this module (for example, MC_SHIFT). | 
					
						
							|  |  |  | Using these members is absolutely portable. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The order by which events are called is defined by these rules: | 
					
						
							|  |  |  | 1. A more-specific event will be called before a less-specific event. | 
					
						
							|  |  |  | 2. A recently-binded event will be called before a previously-binded event, | 
					
						
							|  |  |  |    unless this conflicts with the first rule. | 
					
						
							|  |  |  | Each function will be called at most once for each event. | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | import string | 
					
						
							|  |  |  | import re | 
					
						
							| 
									
										
										
										
											2008-05-20 07:13:37 +00:00
										 |  |  | import Tkinter | 
					
						
							| 
									
										
										
										
											2009-03-04 21:35:05 +00:00
										 |  |  | import macosxSupport | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # the event type constants, which define the meaning of mc_type | 
					
						
							|  |  |  | MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3; | 
					
						
							|  |  |  | MC_ACTIVATE=4; MC_CIRCULATE=5; MC_COLORMAP=6; MC_CONFIGURE=7; | 
					
						
							|  |  |  | MC_DEACTIVATE=8; MC_DESTROY=9; MC_ENTER=10; MC_EXPOSE=11; MC_FOCUSIN=12; | 
					
						
							|  |  |  | MC_FOCUSOUT=13; MC_GRAVITY=14; MC_LEAVE=15; MC_MAP=16; MC_MOTION=17; | 
					
						
							|  |  |  | MC_MOUSEWHEEL=18; MC_PROPERTY=19; MC_REPARENT=20; MC_UNMAP=21; MC_VISIBILITY=22; | 
					
						
							|  |  |  | # the modifier state constants, which define the meaning of mc_state | 
					
						
							|  |  |  | MC_SHIFT = 1<<0; MC_CONTROL = 1<<2; MC_ALT = 1<<3; MC_META = 1<<5 | 
					
						
							|  |  |  | MC_OPTION = 1<<6; MC_COMMAND = 1<<7 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # define the list of modifiers, to be used in complex event types. | 
					
						
							| 
									
										
										
										
											2009-03-04 21:35:05 +00:00
										 |  |  | if macosxSupport.runningAsOSXApp(): | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |     _modifiers = (("Shift",), ("Control",), ("Option",), ("Command",)) | 
					
						
							|  |  |  |     _modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND) | 
					
						
							|  |  |  | else: | 
					
						
							|  |  |  |     _modifiers = (("Control",), ("Alt",), ("Shift",), ("Meta", "M")) | 
					
						
							|  |  |  |     _modifier_masks = (MC_CONTROL, MC_ALT, MC_SHIFT, MC_META) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # a dictionary to map a modifier name into its number | 
					
						
							|  |  |  | _modifier_names = dict([(name, number) | 
					
						
							|  |  |  |                          for number in range(len(_modifiers)) | 
					
						
							|  |  |  |                          for name in _modifiers[number]]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # A binder is a class which binds functions to one type of event. It has two | 
					
						
							|  |  |  | # methods: bind and unbind, which get a function and a parsed sequence, as | 
					
						
							|  |  |  | # returned by _parse_sequence(). There are two types of binders: | 
					
						
							|  |  |  | # _SimpleBinder handles event types with no modifiers and no detail. | 
					
						
							|  |  |  | # No Python functions are called when no events are binded. | 
					
						
							|  |  |  | # _ComplexBinder handles event types with modifiers and a detail. | 
					
						
							|  |  |  | # A Python function is called each time an event is generated. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _SimpleBinder: | 
					
						
							|  |  |  |     def __init__(self, type, widget, widgetinst): | 
					
						
							|  |  |  |         self.type = type | 
					
						
							|  |  |  |         self.sequence = '<'+_types[type][0]+'>' | 
					
						
							|  |  |  |         self.widget = widget | 
					
						
							|  |  |  |         self.widgetinst = widgetinst | 
					
						
							|  |  |  |         self.bindedfuncs = [] | 
					
						
							|  |  |  |         self.handlerid = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def bind(self, triplet, func): | 
					
						
							|  |  |  |         if not self.handlerid: | 
					
						
							|  |  |  |             def handler(event, l = self.bindedfuncs, mc_type = self.type): | 
					
						
							|  |  |  |                 event.mc_type = mc_type | 
					
						
							|  |  |  |                 wascalled = {} | 
					
						
							|  |  |  |                 for i in range(len(l)-1, -1, -1): | 
					
						
							|  |  |  |                     func = l[i] | 
					
						
							|  |  |  |                     if func not in wascalled: | 
					
						
							|  |  |  |                         wascalled[func] = True | 
					
						
							|  |  |  |                         r = func(event) | 
					
						
							|  |  |  |                         if r: | 
					
						
							|  |  |  |                             return r | 
					
						
							|  |  |  |             self.handlerid = self.widget.bind(self.widgetinst, | 
					
						
							|  |  |  |                                               self.sequence, handler) | 
					
						
							|  |  |  |         self.bindedfuncs.append(func) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def unbind(self, triplet, func): | 
					
						
							|  |  |  |         self.bindedfuncs.remove(func) | 
					
						
							|  |  |  |         if not self.bindedfuncs: | 
					
						
							|  |  |  |             self.widget.unbind(self.widgetinst, self.sequence, self.handlerid) | 
					
						
							|  |  |  |             self.handlerid = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __del__(self): | 
					
						
							|  |  |  |         if self.handlerid: | 
					
						
							|  |  |  |             self.widget.unbind(self.widgetinst, self.sequence, self.handlerid) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # An int in range(1 << len(_modifiers)) represents a combination of modifiers | 
					
						
							|  |  |  | # (if the least significent bit is on, _modifiers[0] is on, and so on). | 
					
						
							|  |  |  | # _state_subsets gives for each combination of modifiers, or *state*, | 
					
						
							|  |  |  | # a list of the states which are a subset of it. This list is ordered by the | 
					
						
							|  |  |  | # number of modifiers is the state - the most specific state comes first. | 
					
						
							|  |  |  | _states = range(1 << len(_modifiers)) | 
					
						
							|  |  |  | _state_names = [reduce(lambda x, y: x + y, | 
					
						
							|  |  |  |                        [_modifiers[i][0]+'-' for i in range(len(_modifiers)) | 
					
						
							|  |  |  |                         if (1 << i) & s], | 
					
						
							|  |  |  |                        "") | 
					
						
							|  |  |  |                 for s in _states] | 
					
						
							|  |  |  | _state_subsets = map(lambda i: filter(lambda j: not (j & (~i)), _states), | 
					
						
							|  |  |  |                       _states) | 
					
						
							|  |  |  | for l in _state_subsets: | 
					
						
							|  |  |  |     l.sort(lambda a, b, nummod = lambda x: len(filter(lambda i: (1<<i) & x, | 
					
						
							|  |  |  |                                                       range(len(_modifiers)))): | 
					
						
							|  |  |  |            nummod(b) - nummod(a)) | 
					
						
							|  |  |  | # _state_codes gives for each state, the portable code to be passed as mc_state | 
					
						
							|  |  |  | _state_codes = [reduce(lambda x, y: x | y, | 
					
						
							|  |  |  |                        [_modifier_masks[i] for i in range(len(_modifiers)) | 
					
						
							|  |  |  |                         if (1 << i) & s], | 
					
						
							|  |  |  |                        0) | 
					
						
							|  |  |  |                 for s in _states] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _ComplexBinder: | 
					
						
							|  |  |  |     # This class binds many functions, and only unbinds them when it is deleted. | 
					
						
							|  |  |  |     # self.handlerids is the list of seqs and ids of binded handler functions. | 
					
						
							|  |  |  |     # The binded functions sit in a dictionary of lists of lists, which maps | 
					
						
							|  |  |  |     # a detail (or None) and a state into a list of functions. | 
					
						
							|  |  |  |     # When a new detail is discovered, handlers for all the possible states | 
					
						
							|  |  |  |     # are binded. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __create_handler(self, lists, mc_type, mc_state): | 
					
						
							|  |  |  |         def handler(event, lists = lists, | 
					
						
							|  |  |  |                     mc_type = mc_type, mc_state = mc_state, | 
					
						
							|  |  |  |                     ishandlerrunning = self.ishandlerrunning, | 
					
						
							|  |  |  |                     doafterhandler = self.doafterhandler): | 
					
						
							|  |  |  |             ishandlerrunning[:] = [True] | 
					
						
							|  |  |  |             event.mc_type = mc_type | 
					
						
							|  |  |  |             event.mc_state = mc_state | 
					
						
							|  |  |  |             wascalled = {} | 
					
						
							|  |  |  |             r = None | 
					
						
							|  |  |  |             for l in lists: | 
					
						
							|  |  |  |                 for i in range(len(l)-1, -1, -1): | 
					
						
							|  |  |  |                     func = l[i] | 
					
						
							|  |  |  |                     if func not in wascalled: | 
					
						
							|  |  |  |                         wascalled[func] = True | 
					
						
							|  |  |  |                         r = l[i](event) | 
					
						
							|  |  |  |                         if r: | 
					
						
							|  |  |  |                             break | 
					
						
							|  |  |  |                 if r: | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |             ishandlerrunning[:] = [] | 
					
						
							|  |  |  |             # Call all functions in doafterhandler and remove them from list | 
					
						
							|  |  |  |             while doafterhandler: | 
					
						
							|  |  |  |                 doafterhandler.pop()() | 
					
						
							|  |  |  |             if r: | 
					
						
							|  |  |  |                 return r | 
					
						
							|  |  |  |         return handler | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, type, widget, widgetinst): | 
					
						
							|  |  |  |         self.type = type | 
					
						
							|  |  |  |         self.typename = _types[type][0] | 
					
						
							|  |  |  |         self.widget = widget | 
					
						
							|  |  |  |         self.widgetinst = widgetinst | 
					
						
							|  |  |  |         self.bindedfuncs = {None: [[] for s in _states]} | 
					
						
							|  |  |  |         self.handlerids = [] | 
					
						
							|  |  |  |         # we don't want to change the lists of functions while a handler is | 
					
						
							|  |  |  |         # running - it will mess up the loop and anyway, we usually want the | 
					
						
							|  |  |  |         # change to happen from the next event. So we have a list of functions | 
					
						
							|  |  |  |         # for the handler to run after it finishes calling the binded functions. | 
					
						
							|  |  |  |         # It calls them only once. | 
					
						
							|  |  |  |         # ishandlerrunning is a list. An empty one means no, otherwise - yes. | 
					
						
							|  |  |  |         # this is done so that it would be mutable. | 
					
						
							|  |  |  |         self.ishandlerrunning = [] | 
					
						
							|  |  |  |         self.doafterhandler = [] | 
					
						
							|  |  |  |         for s in _states: | 
					
						
							|  |  |  |             lists = [self.bindedfuncs[None][i] for i in _state_subsets[s]] | 
					
						
							|  |  |  |             handler = self.__create_handler(lists, type, _state_codes[s]) | 
					
						
							|  |  |  |             seq = '<'+_state_names[s]+self.typename+'>' | 
					
						
							|  |  |  |             self.handlerids.append((seq, self.widget.bind(self.widgetinst, | 
					
						
							|  |  |  |                                                           seq, handler))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def bind(self, triplet, func): | 
					
						
							| 
									
										
										
										
											2009-10-09 22:15:50 +00:00
										 |  |  |         if triplet[2] not in self.bindedfuncs: | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |             self.bindedfuncs[triplet[2]] = [[] for s in _states] | 
					
						
							|  |  |  |             for s in _states: | 
					
						
							|  |  |  |                 lists = [ self.bindedfuncs[detail][i] | 
					
						
							|  |  |  |                           for detail in (triplet[2], None) | 
					
						
							|  |  |  |                           for i in _state_subsets[s]       ] | 
					
						
							|  |  |  |                 handler = self.__create_handler(lists, self.type, | 
					
						
							|  |  |  |                                                 _state_codes[s]) | 
					
						
							|  |  |  |                 seq = "<%s%s-%s>"% (_state_names[s], self.typename, triplet[2]) | 
					
						
							|  |  |  |                 self.handlerids.append((seq, self.widget.bind(self.widgetinst, | 
					
						
							|  |  |  |                                                               seq, handler))) | 
					
						
							|  |  |  |         doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].append(func) | 
					
						
							|  |  |  |         if not self.ishandlerrunning: | 
					
						
							|  |  |  |             doit() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.doafterhandler.append(doit) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def unbind(self, triplet, func): | 
					
						
							|  |  |  |         doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].remove(func) | 
					
						
							|  |  |  |         if not self.ishandlerrunning: | 
					
						
							|  |  |  |             doit() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.doafterhandler.append(doit) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __del__(self): | 
					
						
							|  |  |  |         for seq, id in self.handlerids: | 
					
						
							|  |  |  |             self.widget.unbind(self.widgetinst, seq, id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # define the list of event types to be handled by MultiEvent. the order is | 
					
						
							|  |  |  | # compatible with the definition of event type constants. | 
					
						
							|  |  |  | _types = ( | 
					
						
							|  |  |  |     ("KeyPress", "Key"), ("KeyRelease",), ("ButtonPress", "Button"), | 
					
						
							|  |  |  |     ("ButtonRelease",), ("Activate",), ("Circulate",), ("Colormap",), | 
					
						
							|  |  |  |     ("Configure",), ("Deactivate",), ("Destroy",), ("Enter",), ("Expose",), | 
					
						
							|  |  |  |     ("FocusIn",), ("FocusOut",), ("Gravity",), ("Leave",), ("Map",), | 
					
						
							|  |  |  |     ("Motion",), ("MouseWheel",), ("Property",), ("Reparent",), ("Unmap",), | 
					
						
							|  |  |  |     ("Visibility",), | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # which binder should be used for every event type? | 
					
						
							|  |  |  | _binder_classes = (_ComplexBinder,) * 4 + (_SimpleBinder,) * (len(_types)-4) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # A dictionary to map a type name into its number | 
					
						
							|  |  |  | _type_names = dict([(name, number) | 
					
						
							|  |  |  |                      for number in range(len(_types)) | 
					
						
							|  |  |  |                      for name in _types[number]]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _keysym_re = re.compile(r"^\w+$") | 
					
						
							|  |  |  | _button_re = re.compile(r"^[1-5]$") | 
					
						
							|  |  |  | def _parse_sequence(sequence): | 
					
						
							|  |  |  |     """Get a string which should describe an event sequence. If it is
 | 
					
						
							|  |  |  |     successfully parsed as one, return a tuple containing the state (as an int), | 
					
						
							|  |  |  |     the event type (as an index of _types), and the detail - None if none, or a | 
					
						
							|  |  |  |     string if there is one. If the parsing is unsuccessful, return None. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not sequence or sequence[0] != '<' or sequence[-1] != '>': | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  |     words = string.split(sequence[1:-1], '-') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     modifiers = 0 | 
					
						
							|  |  |  |     while words and words[0] in _modifier_names: | 
					
						
							|  |  |  |         modifiers |= 1 << _modifier_names[words[0]] | 
					
						
							|  |  |  |         del words[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if words and words[0] in _type_names: | 
					
						
							|  |  |  |         type = _type_names[words[0]] | 
					
						
							|  |  |  |         del words[0] | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if _binder_classes[type] is _SimpleBinder: | 
					
						
							|  |  |  |         if modifiers or words: | 
					
						
							|  |  |  |             return None | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             detail = None | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         # _ComplexBinder | 
					
						
							|  |  |  |         if type in [_type_names[s] for s in ("KeyPress", "KeyRelease")]: | 
					
						
							|  |  |  |             type_re = _keysym_re | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             type_re = _button_re | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not words: | 
					
						
							|  |  |  |             detail = None | 
					
						
							|  |  |  |         elif len(words) == 1 and type_re.match(words[0]): | 
					
						
							|  |  |  |             detail = words[0] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return modifiers, type, detail | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _triplet_to_sequence(triplet): | 
					
						
							|  |  |  |     if triplet[2]: | 
					
						
							|  |  |  |         return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'-'+ \ | 
					
						
							|  |  |  |                triplet[2]+'>' | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'>' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _multicall_dict = {} | 
					
						
							|  |  |  | def MultiCallCreator(widget): | 
					
						
							|  |  |  |     """Return a MultiCall class which inherits its methods from the
 | 
					
						
							|  |  |  |     given widget class (for example, Tkinter.Text). This is used | 
					
						
							|  |  |  |     instead of a templating mechanism. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if widget in _multicall_dict: | 
					
						
							|  |  |  |         return _multicall_dict[widget] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class MultiCall (widget): | 
					
						
							| 
									
										
										
										
											2008-05-20 07:13:37 +00:00
										 |  |  |         assert issubclass(widget, Tkinter.Misc) | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         def __init__(self, *args, **kwargs): | 
					
						
							|  |  |  |             apply(widget.__init__, (self,)+args, kwargs) | 
					
						
							|  |  |  |             # a dictionary which maps a virtual event to a tuple with: | 
					
						
							|  |  |  |             #  0. the function binded | 
					
						
							|  |  |  |             #  1. a list of triplets - the sequences it is binded to | 
					
						
							|  |  |  |             self.__eventinfo = {} | 
					
						
							|  |  |  |             self.__binders = [_binder_classes[i](i, widget, self) | 
					
						
							|  |  |  |                               for i in range(len(_types))] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def bind(self, sequence=None, func=None, add=None): | 
					
						
							|  |  |  |             #print "bind(%s, %s, %s) called." % (sequence, func, add) | 
					
						
							|  |  |  |             if type(sequence) is str and len(sequence) > 2 and \ | 
					
						
							|  |  |  |                sequence[:2] == "<<" and sequence[-2:] == ">>": | 
					
						
							|  |  |  |                 if sequence in self.__eventinfo: | 
					
						
							|  |  |  |                     ei = self.__eventinfo[sequence] | 
					
						
							|  |  |  |                     if ei[0] is not None: | 
					
						
							|  |  |  |                         for triplet in ei[1]: | 
					
						
							|  |  |  |                             self.__binders[triplet[1]].unbind(triplet, ei[0]) | 
					
						
							|  |  |  |                     ei[0] = func | 
					
						
							|  |  |  |                     if ei[0] is not None: | 
					
						
							|  |  |  |                         for triplet in ei[1]: | 
					
						
							|  |  |  |                             self.__binders[triplet[1]].bind(triplet, func) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     self.__eventinfo[sequence] = [func, []] | 
					
						
							|  |  |  |             return widget.bind(self, sequence, func, add) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def unbind(self, sequence, funcid=None): | 
					
						
							|  |  |  |             if type(sequence) is str and len(sequence) > 2 and \ | 
					
						
							|  |  |  |                sequence[:2] == "<<" and sequence[-2:] == ">>" and \ | 
					
						
							|  |  |  |                sequence in self.__eventinfo: | 
					
						
							|  |  |  |                 func, triplets = self.__eventinfo[sequence] | 
					
						
							|  |  |  |                 if func is not None: | 
					
						
							|  |  |  |                     for triplet in triplets: | 
					
						
							|  |  |  |                         self.__binders[triplet[1]].unbind(triplet, func) | 
					
						
							|  |  |  |                     self.__eventinfo[sequence][0] = None | 
					
						
							|  |  |  |             return widget.unbind(self, sequence, funcid) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def event_add(self, virtual, *sequences): | 
					
						
							|  |  |  |             #print "event_add(%s,%s) was called"%(repr(virtual),repr(sequences)) | 
					
						
							|  |  |  |             if virtual not in self.__eventinfo: | 
					
						
							|  |  |  |                 self.__eventinfo[virtual] = [None, []] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             func, triplets = self.__eventinfo[virtual] | 
					
						
							|  |  |  |             for seq in sequences: | 
					
						
							|  |  |  |                 triplet = _parse_sequence(seq) | 
					
						
							|  |  |  |                 if triplet is None: | 
					
						
							|  |  |  |                     #print >> sys.stderr, "Seq. %s was added by Tkinter."%seq | 
					
						
							|  |  |  |                     widget.event_add(self, virtual, seq) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     if func is not None: | 
					
						
							|  |  |  |                         self.__binders[triplet[1]].bind(triplet, func) | 
					
						
							|  |  |  |                     triplets.append(triplet) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def event_delete(self, virtual, *sequences): | 
					
						
							| 
									
										
										
										
											2007-03-06 11:52:24 +00:00
										 |  |  |             if virtual not in self.__eventinfo: | 
					
						
							|  |  |  |                 return | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |             func, triplets = self.__eventinfo[virtual] | 
					
						
							|  |  |  |             for seq in sequences: | 
					
						
							|  |  |  |                 triplet = _parse_sequence(seq) | 
					
						
							|  |  |  |                 if triplet is None: | 
					
						
							|  |  |  |                     #print >> sys.stderr, "Seq. %s was deleted by Tkinter."%seq | 
					
						
							|  |  |  |                     widget.event_delete(self, virtual, seq) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     if func is not None: | 
					
						
							|  |  |  |                         self.__binders[triplet[1]].unbind(triplet, func) | 
					
						
							|  |  |  |                     triplets.remove(triplet) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def event_info(self, virtual=None): | 
					
						
							|  |  |  |             if virtual is None or virtual not in self.__eventinfo: | 
					
						
							|  |  |  |                 return widget.event_info(self, virtual) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 return tuple(map(_triplet_to_sequence, | 
					
						
							|  |  |  |                                  self.__eventinfo[virtual][1])) + \ | 
					
						
							|  |  |  |                        widget.event_info(self, virtual) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def __del__(self): | 
					
						
							|  |  |  |             for virtual in self.__eventinfo: | 
					
						
							|  |  |  |                 func, triplets = self.__eventinfo[virtual] | 
					
						
							|  |  |  |                 if func: | 
					
						
							|  |  |  |                     for triplet in triplets: | 
					
						
							|  |  |  |                         self.__binders[triplet[1]].unbind(triplet, func) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _multicall_dict[widget] = MultiCall | 
					
						
							|  |  |  |     return MultiCall | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     # Test | 
					
						
							| 
									
										
										
										
											2008-05-20 07:13:37 +00:00
										 |  |  |     root = Tkinter.Tk() | 
					
						
							|  |  |  |     text = MultiCallCreator(Tkinter.Text)(root) | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |     text.pack() | 
					
						
							|  |  |  |     def bindseq(seq, n=[0]): | 
					
						
							|  |  |  |         def handler(event): | 
					
						
							|  |  |  |             print seq | 
					
						
							|  |  |  |         text.bind("<<handler%d>>"%n[0], handler) | 
					
						
							|  |  |  |         text.event_add("<<handler%d>>"%n[0], seq) | 
					
						
							|  |  |  |         n[0] += 1 | 
					
						
							|  |  |  |     bindseq("<Key>") | 
					
						
							|  |  |  |     bindseq("<Control-Key>") | 
					
						
							|  |  |  |     bindseq("<Alt-Key-a>") | 
					
						
							|  |  |  |     bindseq("<Control-Key-a>") | 
					
						
							|  |  |  |     bindseq("<Alt-Control-Key-a>") | 
					
						
							|  |  |  |     bindseq("<Key-b>") | 
					
						
							|  |  |  |     bindseq("<Control-Button-1>") | 
					
						
							|  |  |  |     bindseq("<Alt-Button-1>") | 
					
						
							|  |  |  |     bindseq("<FocusOut>") | 
					
						
							|  |  |  |     bindseq("<Enter>") | 
					
						
							|  |  |  |     bindseq("<Leave>") | 
					
						
							|  |  |  |     root.mainloop() |