Skip to content

Commit 360d15a

Browse files
serhiy-storchakaGiovaLomba
authored andcommitted
pythongh-75666: Tkinter: "unbind(sequence, funcid)" now only unbinds "funcid" (pythonGH-111322)
Previously, "widget.unbind(sequence, funcid)" destroyed the current binding for "sequence", leaving "sequence" unbound, and deleted the "funcid" command. Now it removes only "funcid" from the binding for "sequence", keeping other commands, and deletes the "funcid" command. It leaves "sequence" unbound only if "funcid" was the last bound command. Co-authored-by: GiovanniL <[email protected]>
1 parent 7569396 commit 360d15a

File tree

3 files changed

+51
-11
lines changed

3 files changed

+51
-11
lines changed

Lib/test/test_tkinter/test_misc.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -479,26 +479,46 @@ def test2(e): pass
479479

480480
def test_unbind2(self):
481481
f = self.frame
482+
f.wait_visibility()
483+
f.focus_force()
484+
f.update_idletasks()
482485
event = '<Control-Alt-Key-c>'
483486
self.assertEqual(f.bind(), ())
484487
self.assertEqual(f.bind(event), '')
485-
def test1(e): pass
486-
def test2(e): pass
488+
def test1(e): events.append('a')
489+
def test2(e): events.append('b')
490+
def test3(e): events.append('c')
487491

488492
funcid = f.bind(event, test1)
489493
funcid2 = f.bind(event, test2, add=True)
494+
funcid3 = f.bind(event, test3, add=True)
495+
events = []
496+
f.event_generate(event)
497+
self.assertEqual(events, ['a', 'b', 'c'])
490498

491-
f.unbind(event, funcid)
499+
f.unbind(event, funcid2)
492500
script = f.bind(event)
493-
self.assertNotIn(funcid, script)
494-
self.assertCommandNotExist(funcid)
495-
self.assertCommandExist(funcid2)
501+
self.assertNotIn(funcid2, script)
502+
self.assertIn(funcid, script)
503+
self.assertIn(funcid3, script)
504+
self.assertEqual(f.bind(), (event,))
505+
self.assertCommandNotExist(funcid2)
506+
self.assertCommandExist(funcid)
507+
self.assertCommandExist(funcid3)
508+
events = []
509+
f.event_generate(event)
510+
self.assertEqual(events, ['a', 'c'])
496511

497-
f.unbind(event, funcid2)
512+
f.unbind(event, funcid)
513+
f.unbind(event, funcid3)
498514
self.assertEqual(f.bind(event), '')
499515
self.assertEqual(f.bind(), ())
500516
self.assertCommandNotExist(funcid)
501517
self.assertCommandNotExist(funcid2)
518+
self.assertCommandNotExist(funcid3)
519+
events = []
520+
f.event_generate(event)
521+
self.assertEqual(events, [])
502522

503523
# non-idempotent
504524
self.assertRaises(tkinter.TclError, f.unbind, event, funcid2)

Lib/tkinter/__init__.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,10 +1527,24 @@ def bind(self, sequence=None, func=None, add=None):
15271527
return self._bind(('bind', self._w), sequence, func, add)
15281528

15291529
def unbind(self, sequence, funcid=None):
1530-
"""Unbind for this widget for event SEQUENCE the
1531-
function identified with FUNCID."""
1532-
self.tk.call('bind', self._w, sequence, '')
1533-
if funcid:
1530+
"""Unbind for this widget the event SEQUENCE.
1531+
1532+
If FUNCID is given, only unbind the function identified with FUNCID
1533+
and also delete the corresponding Tcl command.
1534+
1535+
Otherwise destroy the current binding for SEQUENCE, leaving SEQUENCE
1536+
unbound.
1537+
"""
1538+
if funcid is None:
1539+
self.tk.call('bind', self._w, sequence, '')
1540+
else:
1541+
lines = self.tk.call('bind', self._w, sequence).split('\n')
1542+
prefix = f'if {{"[{funcid} '
1543+
keep = '\n'.join(line for line in lines
1544+
if not line.startswith(prefix))
1545+
if not keep.strip():
1546+
keep = ''
1547+
self.tk.call('bind', self._w, sequence, keep)
15341548
self.deletecommand(funcid)
15351549

15361550
def bind_all(self, sequence=None, func=None, add=None):
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Fix the behavior of :mod:`tkinter` widget's ``unbind()`` method with two
2+
arguments. Previously, ``widget.unbind(sequence, funcid)`` destroyed the
3+
current binding for *sequence*, leaving *sequence* unbound, and deleted the
4+
*funcid* command. Now it removes only *funcid* from the binding for
5+
*sequence*, keeping other commands, and deletes the *funcid* command. It
6+
leaves *sequence* unbound only if *funcid* was the last bound command.

0 commit comments

Comments
 (0)