Skip to content

Commit

Permalink
pythongh-75666: Tkinter: "unbind(sequence, funcid)" now only unbinds …
Browse files Browse the repository at this point in the history
…"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 <13402461+GiovaLomba@users.noreply.github.com>
  • Loading branch information
serhiy-storchaka and GiovaLomba committed Dec 6, 2023
1 parent 828451d commit cc7e45c
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 11 deletions.
34 changes: 27 additions & 7 deletions Lib/test/test_tkinter/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,26 +479,46 @@ def test2(e): pass

def test_unbind2(self):
f = self.frame
f.wait_visibility()
f.focus_force()
f.update_idletasks()
event = '<Control-Alt-Key-c>'
self.assertEqual(f.bind(), ())
self.assertEqual(f.bind(event), '')
def test1(e): pass
def test2(e): pass
def test1(e): events.append('a')
def test2(e): events.append('b')
def test3(e): events.append('c')

funcid = f.bind(event, test1)
funcid2 = f.bind(event, test2, add=True)
funcid3 = f.bind(event, test3, add=True)
events = []
f.event_generate(event)
self.assertEqual(events, ['a', 'b', 'c'])

f.unbind(event, funcid)
f.unbind(event, funcid2)
script = f.bind(event)
self.assertNotIn(funcid, script)
self.assertCommandNotExist(funcid)
self.assertCommandExist(funcid2)
self.assertNotIn(funcid2, script)
self.assertIn(funcid, script)
self.assertIn(funcid3, script)
self.assertEqual(f.bind(), (event,))
self.assertCommandNotExist(funcid2)
self.assertCommandExist(funcid)
self.assertCommandExist(funcid3)
events = []
f.event_generate(event)
self.assertEqual(events, ['a', 'c'])

f.unbind(event, funcid2)
f.unbind(event, funcid)
f.unbind(event, funcid3)
self.assertEqual(f.bind(event), '')
self.assertEqual(f.bind(), ())
self.assertCommandNotExist(funcid)
self.assertCommandNotExist(funcid2)
self.assertCommandNotExist(funcid3)
events = []
f.event_generate(event)
self.assertEqual(events, [])

# non-idempotent
self.assertRaises(tkinter.TclError, f.unbind, event, funcid2)
Expand Down
22 changes: 18 additions & 4 deletions Lib/tkinter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1527,10 +1527,24 @@ def bind(self, sequence=None, func=None, add=None):
return self._bind(('bind', self._w), sequence, func, add)

def unbind(self, sequence, funcid=None):
"""Unbind for this widget for event SEQUENCE the
function identified with FUNCID."""
self.tk.call('bind', self._w, sequence, '')
if funcid:
"""Unbind for this widget the event SEQUENCE.
If FUNCID is given, only unbind the function identified with FUNCID
and also delete the corresponding Tcl command.
Otherwise destroy the current binding for SEQUENCE, leaving SEQUENCE
unbound.
"""
if funcid is None:
self.tk.call('bind', self._w, sequence, '')
else:
lines = self.tk.call('bind', self._w, sequence).split('\n')
prefix = f'if {{"[{funcid} '
keep = '\n'.join(line for line in lines
if not line.startswith(prefix))
if not keep.strip():
keep = ''
self.tk.call('bind', self._w, sequence, keep)
self.deletecommand(funcid)

def bind_all(self, sequence=None, func=None, add=None):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Fix the behavior of :mod:`tkinter` widget's ``unbind()`` method with two
arguments. 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.

0 comments on commit cc7e45c

Please sign in to comment.