ยปCore Development>Code coverage>Lib/idlelib/MultiCall.py

Python code coverage for Lib/idlelib/MultiCall.py

#countcontent
1n/a"""
2n/aMultiCall - a class which inherits its methods from a Tkinter widget (Text, for
3n/aexample), but enables multiple calls of functions per virtual event - all
4n/amatching events will be called, not only the most specific one. This is done
5n/aby wrapping the event functions - event_add, event_delete and event_info.
6n/aMultiCall recognizes only a subset of legal event sequences. Sequences which
7n/aare not recognized are treated by the original Tk handling mechanism. A
8n/amore-specific event will be called before a less-specific event.
9n/a
10n/aThe recognized sequences are complete one-event sequences (no emacs-style
11n/aCtrl-X Ctrl-C, no shortcuts like <3>), for all types of events.
12n/aKey/Button Press/Release events can have modifiers.
13n/aThe recognized modifiers are Shift, Control, Option and Command for Mac, and
14n/aControl, Alt, Shift, Meta/M for other platforms.
15n/a
16n/aFor all events which were handled by MultiCall, a new member is added to the
17n/aevent instance passed to the binded functions - mc_type. This is one of the
18n/aevent type constants defined in this module (such as MC_KEYPRESS).
19n/aFor Key/Button events (which are handled by MultiCall and may receive
20n/amodifiers), another member is added - mc_state. This member gives the state
21n/aof the recognized modifiers, as a combination of the modifier constants
22n/aalso defined in this module (for example, MC_SHIFT).
23n/aUsing these members is absolutely portable.
24n/a
25n/aThe order by which events are called is defined by these rules:
26n/a1. A more-specific event will be called before a less-specific event.
27n/a2. A recently-binded event will be called before a previously-binded event,
28n/a unless this conflicts with the first rule.
29n/aEach function will be called at most once for each event.
30n/a"""
31n/a
32n/aimport sys
33n/aimport re
34n/aimport tkinter
35n/afrom idlelib import macosxSupport
36n/a
37n/a# the event type constants, which define the meaning of mc_type
38n/aMC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3;
39n/aMC_ACTIVATE=4; MC_CIRCULATE=5; MC_COLORMAP=6; MC_CONFIGURE=7;
40n/aMC_DEACTIVATE=8; MC_DESTROY=9; MC_ENTER=10; MC_EXPOSE=11; MC_FOCUSIN=12;
41n/aMC_FOCUSOUT=13; MC_GRAVITY=14; MC_LEAVE=15; MC_MAP=16; MC_MOTION=17;
42n/aMC_MOUSEWHEEL=18; MC_PROPERTY=19; MC_REPARENT=20; MC_UNMAP=21; MC_VISIBILITY=22;
43n/a# the modifier state constants, which define the meaning of mc_state
44n/aMC_SHIFT = 1<<0; MC_CONTROL = 1<<2; MC_ALT = 1<<3; MC_META = 1<<5
45n/aMC_OPTION = 1<<6; MC_COMMAND = 1<<7
46n/a
47n/a# define the list of modifiers, to be used in complex event types.
48n/aif macosxSupport.runningAsOSXApp():
49n/a _modifiers = (("Shift",), ("Control",), ("Option",), ("Command",))
50n/a _modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND)
51n/aelse:
52n/a _modifiers = (("Control",), ("Alt",), ("Shift",), ("Meta", "M"))
53n/a _modifier_masks = (MC_CONTROL, MC_ALT, MC_SHIFT, MC_META)
54n/a
55n/a# a dictionary to map a modifier name into its number
56n/a_modifier_names = dict([(name, number)
57n/a for number in range(len(_modifiers))
58n/a for name in _modifiers[number]])
59n/a
60n/a# A binder is a class which binds functions to one type of event. It has two
61n/a# methods: bind and unbind, which get a function and a parsed sequence, as
62n/a# returned by _parse_sequence(). There are two types of binders:
63n/a# _SimpleBinder handles event types with no modifiers and no detail.
64n/a# No Python functions are called when no events are binded.
65n/a# _ComplexBinder handles event types with modifiers and a detail.
66n/a# A Python function is called each time an event is generated.
67n/a
68n/aclass _SimpleBinder:
69n/a def __init__(self, type, widget, widgetinst):
70n/a self.type = type
71n/a self.sequence = '<'+_types[type][0]+'>'
72n/a self.widget = widget
73n/a self.widgetinst = widgetinst
74n/a self.bindedfuncs = []
75n/a self.handlerid = None
76n/a
77n/a def bind(self, triplet, func):
78n/a if not self.handlerid:
79n/a def handler(event, l = self.bindedfuncs, mc_type = self.type):
80n/a event.mc_type = mc_type
81n/a wascalled = {}
82n/a for i in range(len(l)-1, -1, -1):
83n/a func = l[i]
84n/a if func not in wascalled:
85n/a wascalled[func] = True
86n/a r = func(event)
87n/a if r:
88n/a return r
89n/a self.handlerid = self.widget.bind(self.widgetinst,
90n/a self.sequence, handler)
91n/a self.bindedfuncs.append(func)
92n/a
93n/a def unbind(self, triplet, func):
94n/a self.bindedfuncs.remove(func)
95n/a if not self.bindedfuncs:
96n/a self.widget.unbind(self.widgetinst, self.sequence, self.handlerid)
97n/a self.handlerid = None
98n/a
99n/a def __del__(self):
100n/a if self.handlerid:
101n/a self.widget.unbind(self.widgetinst, self.sequence, self.handlerid)
102n/a
103n/a# An int in range(1 << len(_modifiers)) represents a combination of modifiers
104n/a# (if the least significent bit is on, _modifiers[0] is on, and so on).
105n/a# _state_subsets gives for each combination of modifiers, or *state*,
106n/a# a list of the states which are a subset of it. This list is ordered by the
107n/a# number of modifiers is the state - the most specific state comes first.
108n/a_states = range(1 << len(_modifiers))
109n/a_state_names = [''.join(m[0]+'-'
110n/a for i, m in enumerate(_modifiers)
111n/a if (1 << i) & s)
112n/a for s in _states]
113n/a
114n/adef expand_substates(states):
115n/a '''For each item of states return a list containing all combinations of
116n/a that item with individual bits reset, sorted by the number of set bits.
117n/a '''
118n/a def nbits(n):
119n/a "number of bits set in n base 2"
120n/a nb = 0
121n/a while n:
122n/a n, rem = divmod(n, 2)
123n/a nb += rem
124n/a return nb
125n/a statelist = []
126n/a for state in states:
127n/a substates = list(set(state & x for x in states))
128n/a substates.sort(key=nbits, reverse=True)
129n/a statelist.append(substates)
130n/a return statelist
131n/a
132n/a_state_subsets = expand_substates(_states)
133n/a
134n/a# _state_codes gives for each state, the portable code to be passed as mc_state
135n/a_state_codes = []
136n/afor s in _states:
137n/a r = 0
138n/a for i in range(len(_modifiers)):
139n/a if (1 << i) & s:
140n/a r |= _modifier_masks[i]
141n/a _state_codes.append(r)
142n/a
143n/aclass _ComplexBinder:
144n/a # This class binds many functions, and only unbinds them when it is deleted.
145n/a # self.handlerids is the list of seqs and ids of binded handler functions.
146n/a # The binded functions sit in a dictionary of lists of lists, which maps
147n/a # a detail (or None) and a state into a list of functions.
148n/a # When a new detail is discovered, handlers for all the possible states
149n/a # are binded.
150n/a
151n/a def __create_handler(self, lists, mc_type, mc_state):
152n/a def handler(event, lists = lists,
153n/a mc_type = mc_type, mc_state = mc_state,
154n/a ishandlerrunning = self.ishandlerrunning,
155n/a doafterhandler = self.doafterhandler):
156n/a ishandlerrunning[:] = [True]
157n/a event.mc_type = mc_type
158n/a event.mc_state = mc_state
159n/a wascalled = {}
160n/a r = None
161n/a for l in lists:
162n/a for i in range(len(l)-1, -1, -1):
163n/a func = l[i]
164n/a if func not in wascalled:
165n/a wascalled[func] = True
166n/a r = l[i](event)
167n/a if r:
168n/a break
169n/a if r:
170n/a break
171n/a ishandlerrunning[:] = []
172n/a # Call all functions in doafterhandler and remove them from list
173n/a for f in doafterhandler:
174n/a f()
175n/a doafterhandler[:] = []
176n/a if r:
177n/a return r
178n/a return handler
179n/a
180n/a def __init__(self, type, widget, widgetinst):
181n/a self.type = type
182n/a self.typename = _types[type][0]
183n/a self.widget = widget
184n/a self.widgetinst = widgetinst
185n/a self.bindedfuncs = {None: [[] for s in _states]}
186n/a self.handlerids = []
187n/a # we don't want to change the lists of functions while a handler is
188n/a # running - it will mess up the loop and anyway, we usually want the
189n/a # change to happen from the next event. So we have a list of functions
190n/a # for the handler to run after it finishes calling the binded functions.
191n/a # It calls them only once.
192n/a # ishandlerrunning is a list. An empty one means no, otherwise - yes.
193n/a # this is done so that it would be mutable.
194n/a self.ishandlerrunning = []
195n/a self.doafterhandler = []
196n/a for s in _states:
197n/a lists = [self.bindedfuncs[None][i] for i in _state_subsets[s]]
198n/a handler = self.__create_handler(lists, type, _state_codes[s])
199n/a seq = '<'+_state_names[s]+self.typename+'>'
200n/a self.handlerids.append((seq, self.widget.bind(self.widgetinst,
201n/a seq, handler)))
202n/a
203n/a def bind(self, triplet, func):
204n/a if triplet[2] not in self.bindedfuncs:
205n/a self.bindedfuncs[triplet[2]] = [[] for s in _states]
206n/a for s in _states:
207n/a lists = [ self.bindedfuncs[detail][i]
208n/a for detail in (triplet[2], None)
209n/a for i in _state_subsets[s] ]
210n/a handler = self.__create_handler(lists, self.type,
211n/a _state_codes[s])
212n/a seq = "<%s%s-%s>"% (_state_names[s], self.typename, triplet[2])
213n/a self.handlerids.append((seq, self.widget.bind(self.widgetinst,
214n/a seq, handler)))
215n/a doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].append(func)
216n/a if not self.ishandlerrunning:
217n/a doit()
218n/a else:
219n/a self.doafterhandler.append(doit)
220n/a
221n/a def unbind(self, triplet, func):
222n/a doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].remove(func)
223n/a if not self.ishandlerrunning:
224n/a doit()
225n/a else:
226n/a self.doafterhandler.append(doit)
227n/a
228n/a def __del__(self):
229n/a for seq, id in self.handlerids:
230n/a self.widget.unbind(self.widgetinst, seq, id)
231n/a
232n/a# define the list of event types to be handled by MultiEvent. the order is
233n/a# compatible with the definition of event type constants.
234n/a_types = (
235n/a ("KeyPress", "Key"), ("KeyRelease",), ("ButtonPress", "Button"),
236n/a ("ButtonRelease",), ("Activate",), ("Circulate",), ("Colormap",),
237n/a ("Configure",), ("Deactivate",), ("Destroy",), ("Enter",), ("Expose",),
238n/a ("FocusIn",), ("FocusOut",), ("Gravity",), ("Leave",), ("Map",),
239n/a ("Motion",), ("MouseWheel",), ("Property",), ("Reparent",), ("Unmap",),
240n/a ("Visibility",),
241n/a)
242n/a
243n/a# which binder should be used for every event type?
244n/a_binder_classes = (_ComplexBinder,) * 4 + (_SimpleBinder,) * (len(_types)-4)
245n/a
246n/a# A dictionary to map a type name into its number
247n/a_type_names = dict([(name, number)
248n/a for number in range(len(_types))
249n/a for name in _types[number]])
250n/a
251n/a_keysym_re = re.compile(r"^\w+$")
252n/a_button_re = re.compile(r"^[1-5]$")
253n/adef _parse_sequence(sequence):
254n/a """Get a string which should describe an event sequence. If it is
255n/a successfully parsed as one, return a tuple containing the state (as an int),
256n/a the event type (as an index of _types), and the detail - None if none, or a
257n/a string if there is one. If the parsing is unsuccessful, return None.
258n/a """
259n/a if not sequence or sequence[0] != '<' or sequence[-1] != '>':
260n/a return None
261n/a words = sequence[1:-1].split('-')
262n/a modifiers = 0
263n/a while words and words[0] in _modifier_names:
264n/a modifiers |= 1 << _modifier_names[words[0]]
265n/a del words[0]
266n/a if words and words[0] in _type_names:
267n/a type = _type_names[words[0]]
268n/a del words[0]
269n/a else:
270n/a return None
271n/a if _binder_classes[type] is _SimpleBinder:
272n/a if modifiers or words:
273n/a return None
274n/a else:
275n/a detail = None
276n/a else:
277n/a # _ComplexBinder
278n/a if type in [_type_names[s] for s in ("KeyPress", "KeyRelease")]:
279n/a type_re = _keysym_re
280n/a else:
281n/a type_re = _button_re
282n/a
283n/a if not words:
284n/a detail = None
285n/a elif len(words) == 1 and type_re.match(words[0]):
286n/a detail = words[0]
287n/a else:
288n/a return None
289n/a
290n/a return modifiers, type, detail
291n/a
292n/adef _triplet_to_sequence(triplet):
293n/a if triplet[2]:
294n/a return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'-'+ \
295n/a triplet[2]+'>'
296n/a else:
297n/a return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'>'
298n/a
299n/a_multicall_dict = {}
300n/adef MultiCallCreator(widget):
301n/a """Return a MultiCall class which inherits its methods from the
302n/a given widget class (for example, Tkinter.Text). This is used
303n/a instead of a templating mechanism.
304n/a """
305n/a if widget in _multicall_dict:
306n/a return _multicall_dict[widget]
307n/a
308n/a class MultiCall (widget):
309n/a assert issubclass(widget, tkinter.Misc)
310n/a
311n/a def __init__(self, *args, **kwargs):
312n/a widget.__init__(self, *args, **kwargs)
313n/a # a dictionary which maps a virtual event to a tuple with:
314n/a # 0. the function binded
315n/a # 1. a list of triplets - the sequences it is binded to
316n/a self.__eventinfo = {}
317n/a self.__binders = [_binder_classes[i](i, widget, self)
318n/a for i in range(len(_types))]
319n/a
320n/a def bind(self, sequence=None, func=None, add=None):
321n/a #print("bind(%s, %s, %s)" % (sequence, func, add),
322n/a # file=sys.__stderr__)
323n/a if type(sequence) is str and len(sequence) > 2 and \
324n/a sequence[:2] == "<<" and sequence[-2:] == ">>":
325n/a if sequence in self.__eventinfo:
326n/a ei = self.__eventinfo[sequence]
327n/a if ei[0] is not None:
328n/a for triplet in ei[1]:
329n/a self.__binders[triplet[1]].unbind(triplet, ei[0])
330n/a ei[0] = func
331n/a if ei[0] is not None:
332n/a for triplet in ei[1]:
333n/a self.__binders[triplet[1]].bind(triplet, func)
334n/a else:
335n/a self.__eventinfo[sequence] = [func, []]
336n/a return widget.bind(self, sequence, func, add)
337n/a
338n/a def unbind(self, sequence, funcid=None):
339n/a if type(sequence) is str and len(sequence) > 2 and \
340n/a sequence[:2] == "<<" and sequence[-2:] == ">>" and \
341n/a sequence in self.__eventinfo:
342n/a func, triplets = self.__eventinfo[sequence]
343n/a if func is not None:
344n/a for triplet in triplets:
345n/a self.__binders[triplet[1]].unbind(triplet, func)
346n/a self.__eventinfo[sequence][0] = None
347n/a return widget.unbind(self, sequence, funcid)
348n/a
349n/a def event_add(self, virtual, *sequences):
350n/a #print("event_add(%s, %s)" % (repr(virtual), repr(sequences)),
351n/a # file=sys.__stderr__)
352n/a if virtual not in self.__eventinfo:
353n/a self.__eventinfo[virtual] = [None, []]
354n/a
355n/a func, triplets = self.__eventinfo[virtual]
356n/a for seq in sequences:
357n/a triplet = _parse_sequence(seq)
358n/a if triplet is None:
359n/a #print("Tkinter event_add(%s)" % seq, file=sys.__stderr__)
360n/a widget.event_add(self, virtual, seq)
361n/a else:
362n/a if func is not None:
363n/a self.__binders[triplet[1]].bind(triplet, func)
364n/a triplets.append(triplet)
365n/a
366n/a def event_delete(self, virtual, *sequences):
367n/a if virtual not in self.__eventinfo:
368n/a return
369n/a func, triplets = self.__eventinfo[virtual]
370n/a for seq in sequences:
371n/a triplet = _parse_sequence(seq)
372n/a if triplet is None:
373n/a #print("Tkinter event_delete: %s" % seq, file=sys.__stderr__)
374n/a widget.event_delete(self, virtual, seq)
375n/a else:
376n/a if func is not None:
377n/a self.__binders[triplet[1]].unbind(triplet, func)
378n/a triplets.remove(triplet)
379n/a
380n/a def event_info(self, virtual=None):
381n/a if virtual is None or virtual not in self.__eventinfo:
382n/a return widget.event_info(self, virtual)
383n/a else:
384n/a return tuple(map(_triplet_to_sequence,
385n/a self.__eventinfo[virtual][1])) + \
386n/a widget.event_info(self, virtual)
387n/a
388n/a def __del__(self):
389n/a for virtual in self.__eventinfo:
390n/a func, triplets = self.__eventinfo[virtual]
391n/a if func:
392n/a for triplet in triplets:
393n/a self.__binders[triplet[1]].unbind(triplet, func)
394n/a
395n/a
396n/a _multicall_dict[widget] = MultiCall
397n/a return MultiCall
398n/a
399n/aif __name__ == "__main__":
400n/a # Test
401n/a root = tkinter.Tk()
402n/a text = MultiCallCreator(tkinter.Text)(root)
403n/a text.pack()
404n/a def bindseq(seq, n=[0]):
405n/a def handler(event):
406n/a print(seq)
407n/a text.bind("<<handler%d>>"%n[0], handler)
408n/a text.event_add("<<handler%d>>"%n[0], seq)
409n/a n[0] += 1
410n/a bindseq("<Key>")
411n/a bindseq("<Control-Key>")
412n/a bindseq("<Alt-Key-a>")
413n/a bindseq("<Control-Key-a>")
414n/a bindseq("<Alt-Control-Key-a>")
415n/a bindseq("<Key-b>")
416n/a bindseq("<Control-Button-1>")
417n/a bindseq("<Alt-Button-1>")
418n/a bindseq("<FocusOut>")
419n/a bindseq("<Enter>")
420n/a bindseq("<Leave>")
421n/a root.mainloop()