17db96d56Sopenharmony_ci""" 27db96d56Sopenharmony_ciMultiCall - a class which inherits its methods from a Tkinter widget (Text, for 37db96d56Sopenharmony_ciexample), but enables multiple calls of functions per virtual event - all 47db96d56Sopenharmony_cimatching events will be called, not only the most specific one. This is done 57db96d56Sopenharmony_ciby wrapping the event functions - event_add, event_delete and event_info. 67db96d56Sopenharmony_ciMultiCall recognizes only a subset of legal event sequences. Sequences which 77db96d56Sopenharmony_ciare not recognized are treated by the original Tk handling mechanism. A 87db96d56Sopenharmony_cimore-specific event will be called before a less-specific event. 97db96d56Sopenharmony_ci 107db96d56Sopenharmony_ciThe recognized sequences are complete one-event sequences (no emacs-style 117db96d56Sopenharmony_ciCtrl-X Ctrl-C, no shortcuts like <3>), for all types of events. 127db96d56Sopenharmony_ciKey/Button Press/Release events can have modifiers. 137db96d56Sopenharmony_ciThe recognized modifiers are Shift, Control, Option and Command for Mac, and 147db96d56Sopenharmony_ciControl, Alt, Shift, Meta/M for other platforms. 157db96d56Sopenharmony_ci 167db96d56Sopenharmony_ciFor all events which were handled by MultiCall, a new member is added to the 177db96d56Sopenharmony_cievent instance passed to the binded functions - mc_type. This is one of the 187db96d56Sopenharmony_cievent type constants defined in this module (such as MC_KEYPRESS). 197db96d56Sopenharmony_ciFor Key/Button events (which are handled by MultiCall and may receive 207db96d56Sopenharmony_cimodifiers), another member is added - mc_state. This member gives the state 217db96d56Sopenharmony_ciof the recognized modifiers, as a combination of the modifier constants 227db96d56Sopenharmony_cialso defined in this module (for example, MC_SHIFT). 237db96d56Sopenharmony_ciUsing these members is absolutely portable. 247db96d56Sopenharmony_ci 257db96d56Sopenharmony_ciThe order by which events are called is defined by these rules: 267db96d56Sopenharmony_ci1. A more-specific event will be called before a less-specific event. 277db96d56Sopenharmony_ci2. A recently-binded event will be called before a previously-binded event, 287db96d56Sopenharmony_ci unless this conflicts with the first rule. 297db96d56Sopenharmony_ciEach function will be called at most once for each event. 307db96d56Sopenharmony_ci""" 317db96d56Sopenharmony_ciimport re 327db96d56Sopenharmony_ciimport sys 337db96d56Sopenharmony_ci 347db96d56Sopenharmony_ciimport tkinter 357db96d56Sopenharmony_ci 367db96d56Sopenharmony_ci# the event type constants, which define the meaning of mc_type 377db96d56Sopenharmony_ciMC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3; 387db96d56Sopenharmony_ciMC_ACTIVATE=4; MC_CIRCULATE=5; MC_COLORMAP=6; MC_CONFIGURE=7; 397db96d56Sopenharmony_ciMC_DEACTIVATE=8; MC_DESTROY=9; MC_ENTER=10; MC_EXPOSE=11; MC_FOCUSIN=12; 407db96d56Sopenharmony_ciMC_FOCUSOUT=13; MC_GRAVITY=14; MC_LEAVE=15; MC_MAP=16; MC_MOTION=17; 417db96d56Sopenharmony_ciMC_MOUSEWHEEL=18; MC_PROPERTY=19; MC_REPARENT=20; MC_UNMAP=21; MC_VISIBILITY=22; 427db96d56Sopenharmony_ci# the modifier state constants, which define the meaning of mc_state 437db96d56Sopenharmony_ciMC_SHIFT = 1<<0; MC_CONTROL = 1<<2; MC_ALT = 1<<3; MC_META = 1<<5 447db96d56Sopenharmony_ciMC_OPTION = 1<<6; MC_COMMAND = 1<<7 457db96d56Sopenharmony_ci 467db96d56Sopenharmony_ci# define the list of modifiers, to be used in complex event types. 477db96d56Sopenharmony_ciif sys.platform == "darwin": 487db96d56Sopenharmony_ci _modifiers = (("Shift",), ("Control",), ("Option",), ("Command",)) 497db96d56Sopenharmony_ci _modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND) 507db96d56Sopenharmony_cielse: 517db96d56Sopenharmony_ci _modifiers = (("Control",), ("Alt",), ("Shift",), ("Meta", "M")) 527db96d56Sopenharmony_ci _modifier_masks = (MC_CONTROL, MC_ALT, MC_SHIFT, MC_META) 537db96d56Sopenharmony_ci 547db96d56Sopenharmony_ci# a dictionary to map a modifier name into its number 557db96d56Sopenharmony_ci_modifier_names = {name: number 567db96d56Sopenharmony_ci for number in range(len(_modifiers)) 577db96d56Sopenharmony_ci for name in _modifiers[number]} 587db96d56Sopenharmony_ci 597db96d56Sopenharmony_ci# In 3.4, if no shell window is ever open, the underlying Tk widget is 607db96d56Sopenharmony_ci# destroyed before .__del__ methods here are called. The following 617db96d56Sopenharmony_ci# is used to selectively ignore shutdown exceptions to avoid 627db96d56Sopenharmony_ci# 'Exception ignored' messages. See http://bugs.python.org/issue20167 637db96d56Sopenharmony_ciAPPLICATION_GONE = "application has been destroyed" 647db96d56Sopenharmony_ci 657db96d56Sopenharmony_ci# A binder is a class which binds functions to one type of event. It has two 667db96d56Sopenharmony_ci# methods: bind and unbind, which get a function and a parsed sequence, as 677db96d56Sopenharmony_ci# returned by _parse_sequence(). There are two types of binders: 687db96d56Sopenharmony_ci# _SimpleBinder handles event types with no modifiers and no detail. 697db96d56Sopenharmony_ci# No Python functions are called when no events are binded. 707db96d56Sopenharmony_ci# _ComplexBinder handles event types with modifiers and a detail. 717db96d56Sopenharmony_ci# A Python function is called each time an event is generated. 727db96d56Sopenharmony_ci 737db96d56Sopenharmony_ciclass _SimpleBinder: 747db96d56Sopenharmony_ci def __init__(self, type, widget, widgetinst): 757db96d56Sopenharmony_ci self.type = type 767db96d56Sopenharmony_ci self.sequence = '<'+_types[type][0]+'>' 777db96d56Sopenharmony_ci self.widget = widget 787db96d56Sopenharmony_ci self.widgetinst = widgetinst 797db96d56Sopenharmony_ci self.bindedfuncs = [] 807db96d56Sopenharmony_ci self.handlerid = None 817db96d56Sopenharmony_ci 827db96d56Sopenharmony_ci def bind(self, triplet, func): 837db96d56Sopenharmony_ci if not self.handlerid: 847db96d56Sopenharmony_ci def handler(event, l = self.bindedfuncs, mc_type = self.type): 857db96d56Sopenharmony_ci event.mc_type = mc_type 867db96d56Sopenharmony_ci wascalled = {} 877db96d56Sopenharmony_ci for i in range(len(l)-1, -1, -1): 887db96d56Sopenharmony_ci func = l[i] 897db96d56Sopenharmony_ci if func not in wascalled: 907db96d56Sopenharmony_ci wascalled[func] = True 917db96d56Sopenharmony_ci r = func(event) 927db96d56Sopenharmony_ci if r: 937db96d56Sopenharmony_ci return r 947db96d56Sopenharmony_ci self.handlerid = self.widget.bind(self.widgetinst, 957db96d56Sopenharmony_ci self.sequence, handler) 967db96d56Sopenharmony_ci self.bindedfuncs.append(func) 977db96d56Sopenharmony_ci 987db96d56Sopenharmony_ci def unbind(self, triplet, func): 997db96d56Sopenharmony_ci self.bindedfuncs.remove(func) 1007db96d56Sopenharmony_ci if not self.bindedfuncs: 1017db96d56Sopenharmony_ci self.widget.unbind(self.widgetinst, self.sequence, self.handlerid) 1027db96d56Sopenharmony_ci self.handlerid = None 1037db96d56Sopenharmony_ci 1047db96d56Sopenharmony_ci def __del__(self): 1057db96d56Sopenharmony_ci if self.handlerid: 1067db96d56Sopenharmony_ci try: 1077db96d56Sopenharmony_ci self.widget.unbind(self.widgetinst, self.sequence, 1087db96d56Sopenharmony_ci self.handlerid) 1097db96d56Sopenharmony_ci except tkinter.TclError as e: 1107db96d56Sopenharmony_ci if not APPLICATION_GONE in e.args[0]: 1117db96d56Sopenharmony_ci raise 1127db96d56Sopenharmony_ci 1137db96d56Sopenharmony_ci# An int in range(1 << len(_modifiers)) represents a combination of modifiers 1147db96d56Sopenharmony_ci# (if the least significant bit is on, _modifiers[0] is on, and so on). 1157db96d56Sopenharmony_ci# _state_subsets gives for each combination of modifiers, or *state*, 1167db96d56Sopenharmony_ci# a list of the states which are a subset of it. This list is ordered by the 1177db96d56Sopenharmony_ci# number of modifiers is the state - the most specific state comes first. 1187db96d56Sopenharmony_ci_states = range(1 << len(_modifiers)) 1197db96d56Sopenharmony_ci_state_names = [''.join(m[0]+'-' 1207db96d56Sopenharmony_ci for i, m in enumerate(_modifiers) 1217db96d56Sopenharmony_ci if (1 << i) & s) 1227db96d56Sopenharmony_ci for s in _states] 1237db96d56Sopenharmony_ci 1247db96d56Sopenharmony_cidef expand_substates(states): 1257db96d56Sopenharmony_ci '''For each item of states return a list containing all combinations of 1267db96d56Sopenharmony_ci that item with individual bits reset, sorted by the number of set bits. 1277db96d56Sopenharmony_ci ''' 1287db96d56Sopenharmony_ci def nbits(n): 1297db96d56Sopenharmony_ci "number of bits set in n base 2" 1307db96d56Sopenharmony_ci nb = 0 1317db96d56Sopenharmony_ci while n: 1327db96d56Sopenharmony_ci n, rem = divmod(n, 2) 1337db96d56Sopenharmony_ci nb += rem 1347db96d56Sopenharmony_ci return nb 1357db96d56Sopenharmony_ci statelist = [] 1367db96d56Sopenharmony_ci for state in states: 1377db96d56Sopenharmony_ci substates = list({state & x for x in states}) 1387db96d56Sopenharmony_ci substates.sort(key=nbits, reverse=True) 1397db96d56Sopenharmony_ci statelist.append(substates) 1407db96d56Sopenharmony_ci return statelist 1417db96d56Sopenharmony_ci 1427db96d56Sopenharmony_ci_state_subsets = expand_substates(_states) 1437db96d56Sopenharmony_ci 1447db96d56Sopenharmony_ci# _state_codes gives for each state, the portable code to be passed as mc_state 1457db96d56Sopenharmony_ci_state_codes = [] 1467db96d56Sopenharmony_cifor s in _states: 1477db96d56Sopenharmony_ci r = 0 1487db96d56Sopenharmony_ci for i in range(len(_modifiers)): 1497db96d56Sopenharmony_ci if (1 << i) & s: 1507db96d56Sopenharmony_ci r |= _modifier_masks[i] 1517db96d56Sopenharmony_ci _state_codes.append(r) 1527db96d56Sopenharmony_ci 1537db96d56Sopenharmony_ciclass _ComplexBinder: 1547db96d56Sopenharmony_ci # This class binds many functions, and only unbinds them when it is deleted. 1557db96d56Sopenharmony_ci # self.handlerids is the list of seqs and ids of binded handler functions. 1567db96d56Sopenharmony_ci # The binded functions sit in a dictionary of lists of lists, which maps 1577db96d56Sopenharmony_ci # a detail (or None) and a state into a list of functions. 1587db96d56Sopenharmony_ci # When a new detail is discovered, handlers for all the possible states 1597db96d56Sopenharmony_ci # are binded. 1607db96d56Sopenharmony_ci 1617db96d56Sopenharmony_ci def __create_handler(self, lists, mc_type, mc_state): 1627db96d56Sopenharmony_ci def handler(event, lists = lists, 1637db96d56Sopenharmony_ci mc_type = mc_type, mc_state = mc_state, 1647db96d56Sopenharmony_ci ishandlerrunning = self.ishandlerrunning, 1657db96d56Sopenharmony_ci doafterhandler = self.doafterhandler): 1667db96d56Sopenharmony_ci ishandlerrunning[:] = [True] 1677db96d56Sopenharmony_ci event.mc_type = mc_type 1687db96d56Sopenharmony_ci event.mc_state = mc_state 1697db96d56Sopenharmony_ci wascalled = {} 1707db96d56Sopenharmony_ci r = None 1717db96d56Sopenharmony_ci for l in lists: 1727db96d56Sopenharmony_ci for i in range(len(l)-1, -1, -1): 1737db96d56Sopenharmony_ci func = l[i] 1747db96d56Sopenharmony_ci if func not in wascalled: 1757db96d56Sopenharmony_ci wascalled[func] = True 1767db96d56Sopenharmony_ci r = l[i](event) 1777db96d56Sopenharmony_ci if r: 1787db96d56Sopenharmony_ci break 1797db96d56Sopenharmony_ci if r: 1807db96d56Sopenharmony_ci break 1817db96d56Sopenharmony_ci ishandlerrunning[:] = [] 1827db96d56Sopenharmony_ci # Call all functions in doafterhandler and remove them from list 1837db96d56Sopenharmony_ci for f in doafterhandler: 1847db96d56Sopenharmony_ci f() 1857db96d56Sopenharmony_ci doafterhandler[:] = [] 1867db96d56Sopenharmony_ci if r: 1877db96d56Sopenharmony_ci return r 1887db96d56Sopenharmony_ci return handler 1897db96d56Sopenharmony_ci 1907db96d56Sopenharmony_ci def __init__(self, type, widget, widgetinst): 1917db96d56Sopenharmony_ci self.type = type 1927db96d56Sopenharmony_ci self.typename = _types[type][0] 1937db96d56Sopenharmony_ci self.widget = widget 1947db96d56Sopenharmony_ci self.widgetinst = widgetinst 1957db96d56Sopenharmony_ci self.bindedfuncs = {None: [[] for s in _states]} 1967db96d56Sopenharmony_ci self.handlerids = [] 1977db96d56Sopenharmony_ci # we don't want to change the lists of functions while a handler is 1987db96d56Sopenharmony_ci # running - it will mess up the loop and anyway, we usually want the 1997db96d56Sopenharmony_ci # change to happen from the next event. So we have a list of functions 2007db96d56Sopenharmony_ci # for the handler to run after it finishes calling the binded functions. 2017db96d56Sopenharmony_ci # It calls them only once. 2027db96d56Sopenharmony_ci # ishandlerrunning is a list. An empty one means no, otherwise - yes. 2037db96d56Sopenharmony_ci # this is done so that it would be mutable. 2047db96d56Sopenharmony_ci self.ishandlerrunning = [] 2057db96d56Sopenharmony_ci self.doafterhandler = [] 2067db96d56Sopenharmony_ci for s in _states: 2077db96d56Sopenharmony_ci lists = [self.bindedfuncs[None][i] for i in _state_subsets[s]] 2087db96d56Sopenharmony_ci handler = self.__create_handler(lists, type, _state_codes[s]) 2097db96d56Sopenharmony_ci seq = '<'+_state_names[s]+self.typename+'>' 2107db96d56Sopenharmony_ci self.handlerids.append((seq, self.widget.bind(self.widgetinst, 2117db96d56Sopenharmony_ci seq, handler))) 2127db96d56Sopenharmony_ci 2137db96d56Sopenharmony_ci def bind(self, triplet, func): 2147db96d56Sopenharmony_ci if triplet[2] not in self.bindedfuncs: 2157db96d56Sopenharmony_ci self.bindedfuncs[triplet[2]] = [[] for s in _states] 2167db96d56Sopenharmony_ci for s in _states: 2177db96d56Sopenharmony_ci lists = [ self.bindedfuncs[detail][i] 2187db96d56Sopenharmony_ci for detail in (triplet[2], None) 2197db96d56Sopenharmony_ci for i in _state_subsets[s] ] 2207db96d56Sopenharmony_ci handler = self.__create_handler(lists, self.type, 2217db96d56Sopenharmony_ci _state_codes[s]) 2227db96d56Sopenharmony_ci seq = "<%s%s-%s>"% (_state_names[s], self.typename, triplet[2]) 2237db96d56Sopenharmony_ci self.handlerids.append((seq, self.widget.bind(self.widgetinst, 2247db96d56Sopenharmony_ci seq, handler))) 2257db96d56Sopenharmony_ci doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].append(func) 2267db96d56Sopenharmony_ci if not self.ishandlerrunning: 2277db96d56Sopenharmony_ci doit() 2287db96d56Sopenharmony_ci else: 2297db96d56Sopenharmony_ci self.doafterhandler.append(doit) 2307db96d56Sopenharmony_ci 2317db96d56Sopenharmony_ci def unbind(self, triplet, func): 2327db96d56Sopenharmony_ci doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].remove(func) 2337db96d56Sopenharmony_ci if not self.ishandlerrunning: 2347db96d56Sopenharmony_ci doit() 2357db96d56Sopenharmony_ci else: 2367db96d56Sopenharmony_ci self.doafterhandler.append(doit) 2377db96d56Sopenharmony_ci 2387db96d56Sopenharmony_ci def __del__(self): 2397db96d56Sopenharmony_ci for seq, id in self.handlerids: 2407db96d56Sopenharmony_ci try: 2417db96d56Sopenharmony_ci self.widget.unbind(self.widgetinst, seq, id) 2427db96d56Sopenharmony_ci except tkinter.TclError as e: 2437db96d56Sopenharmony_ci if not APPLICATION_GONE in e.args[0]: 2447db96d56Sopenharmony_ci raise 2457db96d56Sopenharmony_ci 2467db96d56Sopenharmony_ci# define the list of event types to be handled by MultiEvent. the order is 2477db96d56Sopenharmony_ci# compatible with the definition of event type constants. 2487db96d56Sopenharmony_ci_types = ( 2497db96d56Sopenharmony_ci ("KeyPress", "Key"), ("KeyRelease",), ("ButtonPress", "Button"), 2507db96d56Sopenharmony_ci ("ButtonRelease",), ("Activate",), ("Circulate",), ("Colormap",), 2517db96d56Sopenharmony_ci ("Configure",), ("Deactivate",), ("Destroy",), ("Enter",), ("Expose",), 2527db96d56Sopenharmony_ci ("FocusIn",), ("FocusOut",), ("Gravity",), ("Leave",), ("Map",), 2537db96d56Sopenharmony_ci ("Motion",), ("MouseWheel",), ("Property",), ("Reparent",), ("Unmap",), 2547db96d56Sopenharmony_ci ("Visibility",), 2557db96d56Sopenharmony_ci) 2567db96d56Sopenharmony_ci 2577db96d56Sopenharmony_ci# which binder should be used for every event type? 2587db96d56Sopenharmony_ci_binder_classes = (_ComplexBinder,) * 4 + (_SimpleBinder,) * (len(_types)-4) 2597db96d56Sopenharmony_ci 2607db96d56Sopenharmony_ci# A dictionary to map a type name into its number 2617db96d56Sopenharmony_ci_type_names = {name: number 2627db96d56Sopenharmony_ci for number in range(len(_types)) 2637db96d56Sopenharmony_ci for name in _types[number]} 2647db96d56Sopenharmony_ci 2657db96d56Sopenharmony_ci_keysym_re = re.compile(r"^\w+$") 2667db96d56Sopenharmony_ci_button_re = re.compile(r"^[1-5]$") 2677db96d56Sopenharmony_cidef _parse_sequence(sequence): 2687db96d56Sopenharmony_ci """Get a string which should describe an event sequence. If it is 2697db96d56Sopenharmony_ci successfully parsed as one, return a tuple containing the state (as an int), 2707db96d56Sopenharmony_ci the event type (as an index of _types), and the detail - None if none, or a 2717db96d56Sopenharmony_ci string if there is one. If the parsing is unsuccessful, return None. 2727db96d56Sopenharmony_ci """ 2737db96d56Sopenharmony_ci if not sequence or sequence[0] != '<' or sequence[-1] != '>': 2747db96d56Sopenharmony_ci return None 2757db96d56Sopenharmony_ci words = sequence[1:-1].split('-') 2767db96d56Sopenharmony_ci modifiers = 0 2777db96d56Sopenharmony_ci while words and words[0] in _modifier_names: 2787db96d56Sopenharmony_ci modifiers |= 1 << _modifier_names[words[0]] 2797db96d56Sopenharmony_ci del words[0] 2807db96d56Sopenharmony_ci if words and words[0] in _type_names: 2817db96d56Sopenharmony_ci type = _type_names[words[0]] 2827db96d56Sopenharmony_ci del words[0] 2837db96d56Sopenharmony_ci else: 2847db96d56Sopenharmony_ci return None 2857db96d56Sopenharmony_ci if _binder_classes[type] is _SimpleBinder: 2867db96d56Sopenharmony_ci if modifiers or words: 2877db96d56Sopenharmony_ci return None 2887db96d56Sopenharmony_ci else: 2897db96d56Sopenharmony_ci detail = None 2907db96d56Sopenharmony_ci else: 2917db96d56Sopenharmony_ci # _ComplexBinder 2927db96d56Sopenharmony_ci if type in [_type_names[s] for s in ("KeyPress", "KeyRelease")]: 2937db96d56Sopenharmony_ci type_re = _keysym_re 2947db96d56Sopenharmony_ci else: 2957db96d56Sopenharmony_ci type_re = _button_re 2967db96d56Sopenharmony_ci 2977db96d56Sopenharmony_ci if not words: 2987db96d56Sopenharmony_ci detail = None 2997db96d56Sopenharmony_ci elif len(words) == 1 and type_re.match(words[0]): 3007db96d56Sopenharmony_ci detail = words[0] 3017db96d56Sopenharmony_ci else: 3027db96d56Sopenharmony_ci return None 3037db96d56Sopenharmony_ci 3047db96d56Sopenharmony_ci return modifiers, type, detail 3057db96d56Sopenharmony_ci 3067db96d56Sopenharmony_cidef _triplet_to_sequence(triplet): 3077db96d56Sopenharmony_ci if triplet[2]: 3087db96d56Sopenharmony_ci return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'-'+ \ 3097db96d56Sopenharmony_ci triplet[2]+'>' 3107db96d56Sopenharmony_ci else: 3117db96d56Sopenharmony_ci return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'>' 3127db96d56Sopenharmony_ci 3137db96d56Sopenharmony_ci_multicall_dict = {} 3147db96d56Sopenharmony_cidef MultiCallCreator(widget): 3157db96d56Sopenharmony_ci """Return a MultiCall class which inherits its methods from the 3167db96d56Sopenharmony_ci given widget class (for example, Tkinter.Text). This is used 3177db96d56Sopenharmony_ci instead of a templating mechanism. 3187db96d56Sopenharmony_ci """ 3197db96d56Sopenharmony_ci if widget in _multicall_dict: 3207db96d56Sopenharmony_ci return _multicall_dict[widget] 3217db96d56Sopenharmony_ci 3227db96d56Sopenharmony_ci class MultiCall (widget): 3237db96d56Sopenharmony_ci assert issubclass(widget, tkinter.Misc) 3247db96d56Sopenharmony_ci 3257db96d56Sopenharmony_ci def __init__(self, *args, **kwargs): 3267db96d56Sopenharmony_ci widget.__init__(self, *args, **kwargs) 3277db96d56Sopenharmony_ci # a dictionary which maps a virtual event to a tuple with: 3287db96d56Sopenharmony_ci # 0. the function binded 3297db96d56Sopenharmony_ci # 1. a list of triplets - the sequences it is binded to 3307db96d56Sopenharmony_ci self.__eventinfo = {} 3317db96d56Sopenharmony_ci self.__binders = [_binder_classes[i](i, widget, self) 3327db96d56Sopenharmony_ci for i in range(len(_types))] 3337db96d56Sopenharmony_ci 3347db96d56Sopenharmony_ci def bind(self, sequence=None, func=None, add=None): 3357db96d56Sopenharmony_ci #print("bind(%s, %s, %s)" % (sequence, func, add), 3367db96d56Sopenharmony_ci # file=sys.__stderr__) 3377db96d56Sopenharmony_ci if type(sequence) is str and len(sequence) > 2 and \ 3387db96d56Sopenharmony_ci sequence[:2] == "<<" and sequence[-2:] == ">>": 3397db96d56Sopenharmony_ci if sequence in self.__eventinfo: 3407db96d56Sopenharmony_ci ei = self.__eventinfo[sequence] 3417db96d56Sopenharmony_ci if ei[0] is not None: 3427db96d56Sopenharmony_ci for triplet in ei[1]: 3437db96d56Sopenharmony_ci self.__binders[triplet[1]].unbind(triplet, ei[0]) 3447db96d56Sopenharmony_ci ei[0] = func 3457db96d56Sopenharmony_ci if ei[0] is not None: 3467db96d56Sopenharmony_ci for triplet in ei[1]: 3477db96d56Sopenharmony_ci self.__binders[triplet[1]].bind(triplet, func) 3487db96d56Sopenharmony_ci else: 3497db96d56Sopenharmony_ci self.__eventinfo[sequence] = [func, []] 3507db96d56Sopenharmony_ci return widget.bind(self, sequence, func, add) 3517db96d56Sopenharmony_ci 3527db96d56Sopenharmony_ci def unbind(self, sequence, funcid=None): 3537db96d56Sopenharmony_ci if type(sequence) is str and len(sequence) > 2 and \ 3547db96d56Sopenharmony_ci sequence[:2] == "<<" and sequence[-2:] == ">>" and \ 3557db96d56Sopenharmony_ci sequence in self.__eventinfo: 3567db96d56Sopenharmony_ci func, triplets = self.__eventinfo[sequence] 3577db96d56Sopenharmony_ci if func is not None: 3587db96d56Sopenharmony_ci for triplet in triplets: 3597db96d56Sopenharmony_ci self.__binders[triplet[1]].unbind(triplet, func) 3607db96d56Sopenharmony_ci self.__eventinfo[sequence][0] = None 3617db96d56Sopenharmony_ci return widget.unbind(self, sequence, funcid) 3627db96d56Sopenharmony_ci 3637db96d56Sopenharmony_ci def event_add(self, virtual, *sequences): 3647db96d56Sopenharmony_ci #print("event_add(%s, %s)" % (repr(virtual), repr(sequences)), 3657db96d56Sopenharmony_ci # file=sys.__stderr__) 3667db96d56Sopenharmony_ci if virtual not in self.__eventinfo: 3677db96d56Sopenharmony_ci self.__eventinfo[virtual] = [None, []] 3687db96d56Sopenharmony_ci 3697db96d56Sopenharmony_ci func, triplets = self.__eventinfo[virtual] 3707db96d56Sopenharmony_ci for seq in sequences: 3717db96d56Sopenharmony_ci triplet = _parse_sequence(seq) 3727db96d56Sopenharmony_ci if triplet is None: 3737db96d56Sopenharmony_ci #print("Tkinter event_add(%s)" % seq, file=sys.__stderr__) 3747db96d56Sopenharmony_ci widget.event_add(self, virtual, seq) 3757db96d56Sopenharmony_ci else: 3767db96d56Sopenharmony_ci if func is not None: 3777db96d56Sopenharmony_ci self.__binders[triplet[1]].bind(triplet, func) 3787db96d56Sopenharmony_ci triplets.append(triplet) 3797db96d56Sopenharmony_ci 3807db96d56Sopenharmony_ci def event_delete(self, virtual, *sequences): 3817db96d56Sopenharmony_ci if virtual not in self.__eventinfo: 3827db96d56Sopenharmony_ci return 3837db96d56Sopenharmony_ci func, triplets = self.__eventinfo[virtual] 3847db96d56Sopenharmony_ci for seq in sequences: 3857db96d56Sopenharmony_ci triplet = _parse_sequence(seq) 3867db96d56Sopenharmony_ci if triplet is None: 3877db96d56Sopenharmony_ci #print("Tkinter event_delete: %s" % seq, file=sys.__stderr__) 3887db96d56Sopenharmony_ci widget.event_delete(self, virtual, seq) 3897db96d56Sopenharmony_ci else: 3907db96d56Sopenharmony_ci if func is not None: 3917db96d56Sopenharmony_ci self.__binders[triplet[1]].unbind(triplet, func) 3927db96d56Sopenharmony_ci triplets.remove(triplet) 3937db96d56Sopenharmony_ci 3947db96d56Sopenharmony_ci def event_info(self, virtual=None): 3957db96d56Sopenharmony_ci if virtual is None or virtual not in self.__eventinfo: 3967db96d56Sopenharmony_ci return widget.event_info(self, virtual) 3977db96d56Sopenharmony_ci else: 3987db96d56Sopenharmony_ci return tuple(map(_triplet_to_sequence, 3997db96d56Sopenharmony_ci self.__eventinfo[virtual][1])) + \ 4007db96d56Sopenharmony_ci widget.event_info(self, virtual) 4017db96d56Sopenharmony_ci 4027db96d56Sopenharmony_ci def __del__(self): 4037db96d56Sopenharmony_ci for virtual in self.__eventinfo: 4047db96d56Sopenharmony_ci func, triplets = self.__eventinfo[virtual] 4057db96d56Sopenharmony_ci if func: 4067db96d56Sopenharmony_ci for triplet in triplets: 4077db96d56Sopenharmony_ci try: 4087db96d56Sopenharmony_ci self.__binders[triplet[1]].unbind(triplet, func) 4097db96d56Sopenharmony_ci except tkinter.TclError as e: 4107db96d56Sopenharmony_ci if not APPLICATION_GONE in e.args[0]: 4117db96d56Sopenharmony_ci raise 4127db96d56Sopenharmony_ci 4137db96d56Sopenharmony_ci _multicall_dict[widget] = MultiCall 4147db96d56Sopenharmony_ci return MultiCall 4157db96d56Sopenharmony_ci 4167db96d56Sopenharmony_ci 4177db96d56Sopenharmony_cidef _multi_call(parent): # htest # 4187db96d56Sopenharmony_ci top = tkinter.Toplevel(parent) 4197db96d56Sopenharmony_ci top.title("Test MultiCall") 4207db96d56Sopenharmony_ci x, y = map(int, parent.geometry().split('+')[1:]) 4217db96d56Sopenharmony_ci top.geometry("+%d+%d" % (x, y + 175)) 4227db96d56Sopenharmony_ci text = MultiCallCreator(tkinter.Text)(top) 4237db96d56Sopenharmony_ci text.pack() 4247db96d56Sopenharmony_ci def bindseq(seq, n=[0]): 4257db96d56Sopenharmony_ci def handler(event): 4267db96d56Sopenharmony_ci print(seq) 4277db96d56Sopenharmony_ci text.bind("<<handler%d>>"%n[0], handler) 4287db96d56Sopenharmony_ci text.event_add("<<handler%d>>"%n[0], seq) 4297db96d56Sopenharmony_ci n[0] += 1 4307db96d56Sopenharmony_ci bindseq("<Key>") 4317db96d56Sopenharmony_ci bindseq("<Control-Key>") 4327db96d56Sopenharmony_ci bindseq("<Alt-Key-a>") 4337db96d56Sopenharmony_ci bindseq("<Control-Key-a>") 4347db96d56Sopenharmony_ci bindseq("<Alt-Control-Key-a>") 4357db96d56Sopenharmony_ci bindseq("<Key-b>") 4367db96d56Sopenharmony_ci bindseq("<Control-Button-1>") 4377db96d56Sopenharmony_ci bindseq("<Button-2>") 4387db96d56Sopenharmony_ci bindseq("<Alt-Button-1>") 4397db96d56Sopenharmony_ci bindseq("<FocusOut>") 4407db96d56Sopenharmony_ci bindseq("<Enter>") 4417db96d56Sopenharmony_ci bindseq("<Leave>") 4427db96d56Sopenharmony_ci 4437db96d56Sopenharmony_ciif __name__ == "__main__": 4447db96d56Sopenharmony_ci from unittest import main 4457db96d56Sopenharmony_ci main('idlelib.idle_test.test_mainmenu', verbosity=2, exit=False) 4467db96d56Sopenharmony_ci 4477db96d56Sopenharmony_ci from idlelib.idle_test.htest import run 4487db96d56Sopenharmony_ci run(_multi_call) 449