summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_tkinter/test_misc.py95
-rw-r--r--Lib/tkinter/__init__.py26
-rw-r--r--Misc/NEWS.d/next/Library/2024-01-11-20-47-49.gh-issue-113951.AzlqFK.rst7
3 files changed, 117 insertions, 11 deletions
diff --git a/Lib/test/test_tkinter/test_misc.py b/Lib/test/test_tkinter/test_misc.py
index dc8a810..7155350 100644
--- a/Lib/test/test_tkinter/test_misc.py
+++ b/Lib/test/test_tkinter/test_misc.py
@@ -706,6 +706,101 @@ class BindTest(AbstractTkTest, unittest.TestCase):
self.assertCommandExist(funcid2)
self.assertCommandExist(funcid3)
+ def _test_tag_bind(self, w):
+ tag = 'sel'
+ event = '<Control-Alt-Key-a>'
+ w.pack()
+ self.assertRaises(TypeError, w.tag_bind)
+ tag_bind = w._tag_bind if isinstance(w, tkinter.Text) else w.tag_bind
+ if isinstance(w, tkinter.Text):
+ self.assertRaises(TypeError, w.tag_bind, tag)
+ self.assertRaises(TypeError, w.tag_bind, tag, event)
+ self.assertEqual(tag_bind(tag), ())
+ self.assertEqual(tag_bind(tag, event), '')
+ def test1(e): pass
+ def test2(e): pass
+
+ funcid = w.tag_bind(tag, event, test1)
+ self.assertEqual(tag_bind(tag), (event,))
+ script = tag_bind(tag, event)
+ self.assertIn(funcid, script)
+ self.assertCommandExist(funcid)
+
+ funcid2 = w.tag_bind(tag, event, test2, add=True)
+ script = tag_bind(tag, event)
+ self.assertIn(funcid, script)
+ self.assertIn(funcid2, script)
+ self.assertCommandExist(funcid)
+ self.assertCommandExist(funcid2)
+
+ def _test_tag_unbind(self, w):
+ tag = 'sel'
+ event = '<Control-Alt-Key-b>'
+ w.pack()
+ tag_bind = w._tag_bind if isinstance(w, tkinter.Text) else w.tag_bind
+ self.assertEqual(tag_bind(tag), ())
+ self.assertEqual(tag_bind(tag, event), '')
+ def test1(e): pass
+ def test2(e): pass
+
+ funcid = w.tag_bind(tag, event, test1)
+ funcid2 = w.tag_bind(tag, event, test2, add=True)
+
+ self.assertRaises(TypeError, w.tag_unbind, tag)
+ w.tag_unbind(tag, event)
+ self.assertEqual(tag_bind(tag, event), '')
+ self.assertEqual(tag_bind(tag), ())
+
+ def _test_tag_bind_rebind(self, w):
+ tag = 'sel'
+ event = '<Control-Alt-Key-d>'
+ w.pack()
+ tag_bind = w._tag_bind if isinstance(w, tkinter.Text) else w.tag_bind
+ self.assertEqual(tag_bind(tag), ())
+ self.assertEqual(tag_bind(tag, event), '')
+ def test1(e): pass
+ def test2(e): pass
+ def test3(e): pass
+
+ funcid = w.tag_bind(tag, event, test1)
+ funcid2 = w.tag_bind(tag, event, test2, add=True)
+ script = tag_bind(tag, event)
+ self.assertIn(funcid2, script)
+ self.assertIn(funcid, script)
+ self.assertCommandExist(funcid)
+ self.assertCommandExist(funcid2)
+
+ funcid3 = w.tag_bind(tag, event, test3)
+ script = tag_bind(tag, event)
+ self.assertNotIn(funcid, script)
+ self.assertNotIn(funcid2, script)
+ self.assertIn(funcid3, script)
+ self.assertCommandExist(funcid3)
+
+ def test_canvas_tag_bind(self):
+ c = tkinter.Canvas(self.frame)
+ self._test_tag_bind(c)
+
+ def test_canvas_tag_unbind(self):
+ c = tkinter.Canvas(self.frame)
+ self._test_tag_unbind(c)
+
+ def test_canvas_tag_bind_rebind(self):
+ c = tkinter.Canvas(self.frame)
+ self._test_tag_bind_rebind(c)
+
+ def test_text_tag_bind(self):
+ t = tkinter.Text(self.frame)
+ self._test_tag_bind(t)
+
+ def test_text_tag_unbind(self):
+ t = tkinter.Text(self.frame)
+ self._test_tag_unbind(t)
+
+ def test_text_tag_bind_rebind(self):
+ t = tkinter.Text(self.frame)
+ self._test_tag_bind_rebind(t)
+
def test_bindtags(self):
f = self.frame
self.assertEqual(self.root.bindtags(), ('.', 'Tk', 'all'))
diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py
index e0db41d..a1567d3 100644
--- a/Lib/tkinter/__init__.py
+++ b/Lib/tkinter/__init__.py
@@ -1537,16 +1537,19 @@ class Misc:
Otherwise destroy the current binding for SEQUENCE, leaving SEQUENCE
unbound.
"""
+ self._unbind(('bind', self._w, sequence), funcid)
+
+ def _unbind(self, what, funcid=None):
if funcid is None:
- self.tk.call('bind', self._w, sequence, '')
+ self.tk.call(*what, '')
else:
- lines = self.tk.call('bind', self._w, sequence).split('\n')
+ lines = self.tk.call(what).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.tk.call(*what, keep)
self.deletecommand(funcid)
def bind_all(self, sequence=None, func=None, add=None):
@@ -1558,7 +1561,7 @@ class Misc:
def unbind_all(self, sequence):
"""Unbind for all widgets for event SEQUENCE all functions."""
- self.tk.call('bind', 'all' , sequence, '')
+ self._root()._unbind(('bind', 'all', sequence))
def bind_class(self, className, sequence=None, func=None, add=None):
"""Bind to widgets with bindtag CLASSNAME at event
@@ -1573,7 +1576,7 @@ class Misc:
def unbind_class(self, className, sequence):
"""Unbind for all widgets with bindtag CLASSNAME for event SEQUENCE
all functions."""
- self.tk.call('bind', className , sequence, '')
+ self._root()._unbind(('bind', className, sequence))
def mainloop(self, n=0):
"""Call the mainloop of Tk."""
@@ -2885,9 +2888,7 @@ class Canvas(Widget, XView, YView):
def tag_unbind(self, tagOrId, sequence, funcid=None):
"""Unbind for all items with TAGORID for event SEQUENCE the
function identified with FUNCID."""
- self.tk.call(self._w, 'bind', tagOrId, sequence, '')
- if funcid:
- self.deletecommand(funcid)
+ self._unbind((self._w, 'bind', tagOrId, sequence), funcid)
def tag_bind(self, tagOrId, sequence=None, func=None, add=None):
"""Bind to all items with TAGORID at event SEQUENCE a call to function FUNC.
@@ -3997,9 +3998,7 @@ class Text(Widget, XView, YView):
def tag_unbind(self, tagName, sequence, funcid=None):
"""Unbind for all characters with TAGNAME for event SEQUENCE the
function identified with FUNCID."""
- self.tk.call(self._w, 'tag', 'bind', tagName, sequence, '')
- if funcid:
- self.deletecommand(funcid)
+ return self._unbind((self._w, 'tag', 'bind', tagName, sequence), funcid)
def tag_bind(self, tagName, sequence, func, add=None):
"""Bind to all characters with TAGNAME at event SEQUENCE a call to function FUNC.
@@ -4010,6 +4009,11 @@ class Text(Widget, XView, YView):
return self._bind((self._w, 'tag', 'bind', tagName),
sequence, func, add)
+ def _tag_bind(self, tagName, sequence=None, func=None, add=None):
+ # For tests only
+ return self._bind((self._w, 'tag', 'bind', tagName),
+ sequence, func, add)
+
def tag_cget(self, tagName, option):
"""Return the value of OPTION for tag TAGNAME."""
if option[:1] != '-':
diff --git a/Misc/NEWS.d/next/Library/2024-01-11-20-47-49.gh-issue-113951.AzlqFK.rst b/Misc/NEWS.d/next/Library/2024-01-11-20-47-49.gh-issue-113951.AzlqFK.rst
new file mode 100644
index 0000000..e683472
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-01-11-20-47-49.gh-issue-113951.AzlqFK.rst
@@ -0,0 +1,7 @@
+Fix the behavior of ``tag_unbind()`` methods of :class:`tkinter.Text` and
+:class:`tkinter.Canvas` classes with three arguments. Previously,
+``widget.tag_unbind(tag, 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.