10a8c90248264a8b26970b4473770bcc3df8515fJosh Gao""" 20a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMultiCall - a class which inherits its methods from a Tkinter widget (Text, for 30a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoexample), but enables multiple calls of functions per virtual event - all 40a8c90248264a8b26970b4473770bcc3df8515fJosh Gaomatching events will be called, not only the most specific one. This is done 50a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoby wrapping the event functions - event_add, event_delete and event_info. 60a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMultiCall recognizes only a subset of legal event sequences. Sequences which 70a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoare not recognized are treated by the original Tk handling mechanism. A 80a8c90248264a8b26970b4473770bcc3df8515fJosh Gaomore-specific event will be called before a less-specific event. 90a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 100a8c90248264a8b26970b4473770bcc3df8515fJosh GaoThe recognized sequences are complete one-event sequences (no emacs-style 110a8c90248264a8b26970b4473770bcc3df8515fJosh GaoCtrl-X Ctrl-C, no shortcuts like <3>), for all types of events. 120a8c90248264a8b26970b4473770bcc3df8515fJosh GaoKey/Button Press/Release events can have modifiers. 130a8c90248264a8b26970b4473770bcc3df8515fJosh GaoThe recognized modifiers are Shift, Control, Option and Command for Mac, and 140a8c90248264a8b26970b4473770bcc3df8515fJosh GaoControl, Alt, Shift, Meta/M for other platforms. 150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 160a8c90248264a8b26970b4473770bcc3df8515fJosh GaoFor all events which were handled by MultiCall, a new member is added to the 170a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoevent instance passed to the binded functions - mc_type. This is one of the 180a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoevent type constants defined in this module (such as MC_KEYPRESS). 190a8c90248264a8b26970b4473770bcc3df8515fJosh GaoFor Key/Button events (which are handled by MultiCall and may receive 200a8c90248264a8b26970b4473770bcc3df8515fJosh Gaomodifiers), another member is added - mc_state. This member gives the state 210a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoof the recognized modifiers, as a combination of the modifier constants 220a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoalso defined in this module (for example, MC_SHIFT). 230a8c90248264a8b26970b4473770bcc3df8515fJosh GaoUsing these members is absolutely portable. 240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 250a8c90248264a8b26970b4473770bcc3df8515fJosh GaoThe order by which events are called is defined by these rules: 260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao1. A more-specific event will be called before a less-specific event. 270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao2. A recently-binded event will be called before a previously-binded event, 280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao unless this conflicts with the first rule. 290a8c90248264a8b26970b4473770bcc3df8515fJosh GaoEach function will be called at most once for each event. 300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao""" 310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 320a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport sys 330a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport string 340a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport re 350a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport Tkinter 360a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom idlelib import macosxSupport 370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# the event type constants, which define the meaning of mc_type 390a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3; 400a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMC_ACTIVATE=4; MC_CIRCULATE=5; MC_COLORMAP=6; MC_CONFIGURE=7; 410a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMC_DEACTIVATE=8; MC_DESTROY=9; MC_ENTER=10; MC_EXPOSE=11; MC_FOCUSIN=12; 420a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMC_FOCUSOUT=13; MC_GRAVITY=14; MC_LEAVE=15; MC_MAP=16; MC_MOTION=17; 430a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMC_MOUSEWHEEL=18; MC_PROPERTY=19; MC_REPARENT=20; MC_UNMAP=21; MC_VISIBILITY=22; 440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# the modifier state constants, which define the meaning of mc_state 450a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMC_SHIFT = 1<<0; MC_CONTROL = 1<<2; MC_ALT = 1<<3; MC_META = 1<<5 460a8c90248264a8b26970b4473770bcc3df8515fJosh GaoMC_OPTION = 1<<6; MC_COMMAND = 1<<7 470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# define the list of modifiers, to be used in complex event types. 490a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoif macosxSupport.runningAsOSXApp(): 500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao _modifiers = (("Shift",), ("Control",), ("Option",), ("Command",)) 510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao _modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND) 520a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoelse: 530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao _modifiers = (("Control",), ("Alt",), ("Shift",), ("Meta", "M")) 540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao _modifier_masks = (MC_CONTROL, MC_ALT, MC_SHIFT, MC_META) 550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# a dictionary to map a modifier name into its number 570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_modifier_names = dict([(name, number) 580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for number in range(len(_modifiers)) 590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for name in _modifiers[number]]) 600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# A binder is a class which binds functions to one type of event. It has two 620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# methods: bind and unbind, which get a function and a parsed sequence, as 630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# returned by _parse_sequence(). There are two types of binders: 640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# _SimpleBinder handles event types with no modifiers and no detail. 650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# No Python functions are called when no events are binded. 660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# _ComplexBinder handles event types with modifiers and a detail. 670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# A Python function is called each time an event is generated. 680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 690a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass _SimpleBinder: 700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __init__(self, type, widget, widgetinst): 710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.type = type 720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.sequence = '<'+_types[type][0]+'>' 730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.widget = widget 740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.widgetinst = widgetinst 750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.bindedfuncs = [] 760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.handlerid = None 770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def bind(self, triplet, func): 790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not self.handlerid: 800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def handler(event, l = self.bindedfuncs, mc_type = self.type): 810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao event.mc_type = mc_type 820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao wascalled = {} 830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for i in range(len(l)-1, -1, -1): 840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao func = l[i] 850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if func not in wascalled: 860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao wascalled[func] = True 870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao r = func(event) 880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if r: 890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return r 900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.handlerid = self.widget.bind(self.widgetinst, 910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.sequence, handler) 920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.bindedfuncs.append(func) 930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def unbind(self, triplet, func): 950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.bindedfuncs.remove(func) 960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not self.bindedfuncs: 970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.widget.unbind(self.widgetinst, self.sequence, self.handlerid) 980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.handlerid = None 990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __del__(self): 1010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.handlerid: 1020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.widget.unbind(self.widgetinst, self.sequence, self.handlerid) 1030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# An int in range(1 << len(_modifiers)) represents a combination of modifiers 1050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# (if the least significent bit is on, _modifiers[0] is on, and so on). 1060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# _state_subsets gives for each combination of modifiers, or *state*, 1070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# a list of the states which are a subset of it. This list is ordered by the 1080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# number of modifiers is the state - the most specific state comes first. 1090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_states = range(1 << len(_modifiers)) 1100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_state_names = [''.join(m[0]+'-' 1110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for i, m in enumerate(_modifiers) 1120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if (1 << i) & s) 1130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for s in _states] 1140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1150a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef expand_substates(states): 1160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao '''For each item of states return a list containing all combinations of 1170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao that item with individual bits reset, sorted by the number of set bits. 1180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ''' 1190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def nbits(n): 1200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao "number of bits set in n base 2" 1210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao nb = 0 1220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao while n: 1230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao n, rem = divmod(n, 2) 1240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao nb += rem 1250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return nb 1260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao statelist = [] 1270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for state in states: 1280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao substates = list(set(state & x for x in states)) 1290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao substates.sort(key=nbits, reverse=True) 1300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao statelist.append(substates) 1310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return statelist 1320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_state_subsets = expand_substates(_states) 1340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# _state_codes gives for each state, the portable code to be passed as mc_state 1360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_state_codes = [] 1370a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofor s in _states: 1380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao r = 0 1390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for i in range(len(_modifiers)): 1400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if (1 << i) & s: 1410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao r |= _modifier_masks[i] 1420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao _state_codes.append(r) 1430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1440a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass _ComplexBinder: 1450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # This class binds many functions, and only unbinds them when it is deleted. 1460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # self.handlerids is the list of seqs and ids of binded handler functions. 1470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # The binded functions sit in a dictionary of lists of lists, which maps 1480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # a detail (or None) and a state into a list of functions. 1490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # When a new detail is discovered, handlers for all the possible states 1500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # are binded. 1510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __create_handler(self, lists, mc_type, mc_state): 1530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def handler(event, lists = lists, 1540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao mc_type = mc_type, mc_state = mc_state, 1550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ishandlerrunning = self.ishandlerrunning, 1560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao doafterhandler = self.doafterhandler): 1570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ishandlerrunning[:] = [True] 1580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao event.mc_type = mc_type 1590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao event.mc_state = mc_state 1600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao wascalled = {} 1610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao r = None 1620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for l in lists: 1630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for i in range(len(l)-1, -1, -1): 1640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao func = l[i] 1650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if func not in wascalled: 1660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao wascalled[func] = True 1670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao r = l[i](event) 1680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if r: 1690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao break 1700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if r: 1710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao break 1720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ishandlerrunning[:] = [] 1730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Call all functions in doafterhandler and remove them from list 1740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for f in doafterhandler: 1750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f() 1760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao doafterhandler[:] = [] 1770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if r: 1780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return r 1790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return handler 1800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __init__(self, type, widget, widgetinst): 1820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.type = type 1830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.typename = _types[type][0] 1840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.widget = widget 1850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.widgetinst = widgetinst 1860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.bindedfuncs = {None: [[] for s in _states]} 1870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.handlerids = [] 1880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # we don't want to change the lists of functions while a handler is 1890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # running - it will mess up the loop and anyway, we usually want the 1900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # change to happen from the next event. So we have a list of functions 1910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # for the handler to run after it finishes calling the binded functions. 1920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # It calls them only once. 1930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # ishandlerrunning is a list. An empty one means no, otherwise - yes. 1940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # this is done so that it would be mutable. 1950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.ishandlerrunning = [] 1960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.doafterhandler = [] 1970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for s in _states: 1980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao lists = [self.bindedfuncs[None][i] for i in _state_subsets[s]] 1990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao handler = self.__create_handler(lists, type, _state_codes[s]) 2000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao seq = '<'+_state_names[s]+self.typename+'>' 2010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.handlerids.append((seq, self.widget.bind(self.widgetinst, 2020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao seq, handler))) 2030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def bind(self, triplet, func): 2050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if triplet[2] not in self.bindedfuncs: 2060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.bindedfuncs[triplet[2]] = [[] for s in _states] 2070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for s in _states: 2080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao lists = [ self.bindedfuncs[detail][i] 2090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for detail in (triplet[2], None) 2100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for i in _state_subsets[s] ] 2110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao handler = self.__create_handler(lists, self.type, 2120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao _state_codes[s]) 2130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao seq = "<%s%s-%s>"% (_state_names[s], self.typename, triplet[2]) 2140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.handlerids.append((seq, self.widget.bind(self.widgetinst, 2150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao seq, handler))) 2160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].append(func) 2170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not self.ishandlerrunning: 2180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao doit() 2190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 2200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.doafterhandler.append(doit) 2210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def unbind(self, triplet, func): 2230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].remove(func) 2240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not self.ishandlerrunning: 2250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao doit() 2260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 2270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.doafterhandler.append(doit) 2280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __del__(self): 2300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for seq, id in self.handlerids: 2310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.widget.unbind(self.widgetinst, seq, id) 2320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# define the list of event types to be handled by MultiEvent. the order is 2340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# compatible with the definition of event type constants. 2350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_types = ( 2360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ("KeyPress", "Key"), ("KeyRelease",), ("ButtonPress", "Button"), 2370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ("ButtonRelease",), ("Activate",), ("Circulate",), ("Colormap",), 2380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ("Configure",), ("Deactivate",), ("Destroy",), ("Enter",), ("Expose",), 2390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ("FocusIn",), ("FocusOut",), ("Gravity",), ("Leave",), ("Map",), 2400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ("Motion",), ("MouseWheel",), ("Property",), ("Reparent",), ("Unmap",), 2410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ("Visibility",), 2420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao) 2430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# which binder should be used for every event type? 2450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_binder_classes = (_ComplexBinder,) * 4 + (_SimpleBinder,) * (len(_types)-4) 2460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# A dictionary to map a type name into its number 2480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_type_names = dict([(name, number) 2490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for number in range(len(_types)) 2500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for name in _types[number]]) 2510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_keysym_re = re.compile(r"^\w+$") 2530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_button_re = re.compile(r"^[1-5]$") 2540a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef _parse_sequence(sequence): 2550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Get a string which should describe an event sequence. If it is 2560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao successfully parsed as one, return a tuple containing the state (as an int), 2570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao the event type (as an index of _types), and the detail - None if none, or a 2580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao string if there is one. If the parsing is unsuccessful, return None. 2590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 2600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not sequence or sequence[0] != '<' or sequence[-1] != '>': 2610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return None 2620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao words = string.split(sequence[1:-1], '-') 2630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao modifiers = 0 2650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao while words and words[0] in _modifier_names: 2660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao modifiers |= 1 << _modifier_names[words[0]] 2670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao del words[0] 2680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if words and words[0] in _type_names: 2700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao type = _type_names[words[0]] 2710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao del words[0] 2720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 2730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return None 2740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if _binder_classes[type] is _SimpleBinder: 2760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if modifiers or words: 2770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return None 2780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 2790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao detail = None 2800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 2810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # _ComplexBinder 2820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if type in [_type_names[s] for s in ("KeyPress", "KeyRelease")]: 2830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao type_re = _keysym_re 2840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 2850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao type_re = _button_re 2860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not words: 2880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao detail = None 2890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif len(words) == 1 and type_re.match(words[0]): 2900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao detail = words[0] 2910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 2920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return None 2930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return modifiers, type, detail 2950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2960a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef _triplet_to_sequence(triplet): 2970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if triplet[2]: 2980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'-'+ \ 2990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao triplet[2]+'>' 3000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 3010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'>' 3020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao_multicall_dict = {} 3040a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef MultiCallCreator(widget): 3050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Return a MultiCall class which inherits its methods from the 3060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao given widget class (for example, Tkinter.Text). This is used 3070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao instead of a templating mechanism. 3080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 3090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if widget in _multicall_dict: 3100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return _multicall_dict[widget] 3110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao class MultiCall (widget): 3130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao assert issubclass(widget, Tkinter.Misc) 3140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __init__(self, *args, **kwargs): 3160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao widget.__init__(self, *args, **kwargs) 3170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # a dictionary which maps a virtual event to a tuple with: 3180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 0. the function binded 3190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 1. a list of triplets - the sequences it is binded to 3200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.__eventinfo = {} 3210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.__binders = [_binder_classes[i](i, widget, self) 3220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for i in range(len(_types))] 3230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def bind(self, sequence=None, func=None, add=None): 3250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao #print "bind(%s, %s, %s) called." % (sequence, func, add) 3260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if type(sequence) is str and len(sequence) > 2 and \ 3270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao sequence[:2] == "<<" and sequence[-2:] == ">>": 3280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if sequence in self.__eventinfo: 3290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ei = self.__eventinfo[sequence] 3300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if ei[0] is not None: 3310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for triplet in ei[1]: 3320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.__binders[triplet[1]].unbind(triplet, ei[0]) 3330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ei[0] = func 3340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if ei[0] is not None: 3350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for triplet in ei[1]: 3360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.__binders[triplet[1]].bind(triplet, func) 3370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 3380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.__eventinfo[sequence] = [func, []] 3390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return widget.bind(self, sequence, func, add) 3400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def unbind(self, sequence, funcid=None): 3420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if type(sequence) is str and len(sequence) > 2 and \ 3430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao sequence[:2] == "<<" and sequence[-2:] == ">>" and \ 3440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao sequence in self.__eventinfo: 3450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao func, triplets = self.__eventinfo[sequence] 3460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if func is not None: 3470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for triplet in triplets: 3480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.__binders[triplet[1]].unbind(triplet, func) 3490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.__eventinfo[sequence][0] = None 3500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return widget.unbind(self, sequence, funcid) 3510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def event_add(self, virtual, *sequences): 3530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao #print "event_add(%s,%s) was called"%(repr(virtual),repr(sequences)) 3540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if virtual not in self.__eventinfo: 3550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.__eventinfo[virtual] = [None, []] 3560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao func, triplets = self.__eventinfo[virtual] 3580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for seq in sequences: 3590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao triplet = _parse_sequence(seq) 3600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if triplet is None: 3610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao #print >> sys.stderr, "Seq. %s was added by Tkinter."%seq 3620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao widget.event_add(self, virtual, seq) 3630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 3640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if func is not None: 3650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.__binders[triplet[1]].bind(triplet, func) 3660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao triplets.append(triplet) 3670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def event_delete(self, virtual, *sequences): 3690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if virtual not in self.__eventinfo: 3700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return 3710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao func, triplets = self.__eventinfo[virtual] 3720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for seq in sequences: 3730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao triplet = _parse_sequence(seq) 3740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if triplet is None: 3750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao #print >> sys.stderr, "Seq. %s was deleted by Tkinter."%seq 3760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao widget.event_delete(self, virtual, seq) 3770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 3780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if func is not None: 3790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.__binders[triplet[1]].unbind(triplet, func) 3800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao triplets.remove(triplet) 3810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def event_info(self, virtual=None): 3830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if virtual is None or virtual not in self.__eventinfo: 3840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return widget.event_info(self, virtual) 3850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 3860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return tuple(map(_triplet_to_sequence, 3870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.__eventinfo[virtual][1])) + \ 3880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao widget.event_info(self, virtual) 3890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __del__(self): 3910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for virtual in self.__eventinfo: 3920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao func, triplets = self.__eventinfo[virtual] 3930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if func: 3940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for triplet in triplets: 3950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.__binders[triplet[1]].unbind(triplet, func) 3960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao _multicall_dict[widget] = MultiCall 3990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return MultiCall 4000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4010a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoif __name__ == "__main__": 4020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Test 4030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao root = Tkinter.Tk() 4040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text = MultiCallCreator(Tkinter.Text)(root) 4050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.pack() 4060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def bindseq(seq, n=[0]): 4070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def handler(event): 4080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print seq 4090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.bind("<<handler%d>>"%n[0], handler) 4100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.event_add("<<handler%d>>"%n[0], seq) 4110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao n[0] += 1 4120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao bindseq("<Key>") 4130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao bindseq("<Control-Key>") 4140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao bindseq("<Alt-Key-a>") 4150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao bindseq("<Control-Key-a>") 4160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao bindseq("<Alt-Control-Key-a>") 4170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao bindseq("<Key-b>") 4180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao bindseq("<Control-Button-1>") 4190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao bindseq("<Alt-Button-1>") 4200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao bindseq("<FocusOut>") 4210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao bindseq("<Enter>") 4220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao bindseq("<Leave>") 4230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao root.mainloop() 424