I'm currently writing a color scheme editor. For the preview of the scheme, I use a text widget, where I insert text with the corresponding color tags (which I generate programmatically).
What I want is the following behaviour:
Now here's my problem:
When I click on a tagged text, the callback of the tag is called. So far so good. But then, the callback of the text widget is called as well, although I return "break" in the tags callback method (which should stop further event handling). How can I stop this?
To illustrate this specific problem, I wrote this working example (for Python 2 & 3):
#!/usr/bin/env python
try:
from Tkinter import *
from tkMessageBox import showinfo
except ImportError:
from tkinter import *
from tkinter.messagebox import showinfo
def on_click(event, widget_origin='?'):
showinfo('Click', '"{}"" clicked'.format(widget_origin))
return 'break'
root = Tk()
text = Text(root)
text.pack()
text.insert(CURRENT, 'Some untagged text...\n')
text.bind('<Button-1>', lambda e, w='textwidget': on_click(e, w))
for i in range(5):
tag_name = 'tag_{}'.format(i)
text.tag_config(tag_name)
text.tag_bind(tag_name, '<Button-1>',
lambda e, w=tag_name: on_click(e, w))
text.insert(CURRENT, tag_name + ' ', tag_name)
root.mainloop()
Any help is appreciated!
Edit: Tried Python 2 as well.
Thanks for posting this question and for providing a solution. I can't count how many hours I lost trying to fix up the symptoms created by this behaviour. Weird Tk
design decision that tag_bind
is insenstive to return "break"
.
Following your idea to hijack the Text
widget by binding it with the same event sequence as tag_bind
, I have improved the solution, which enables now to simulate the expected return "break"
behaviour of Tk's other bind+callback pairs. The idea is the following (full source below):
callback
, i.e. a callable class instancecallback
and check its result.
"break"
, temporarily hijack the event propagation: bind
the Text
widget to the same event bound to tag_bind
, with an empty callback. Then, after an idle time, unbind
."break"
: do nothing, the event will propagate to Text
automaticallyHere is a full working example. My specific problem was to get some sort of hyper text behaviour: ctrl-clicking on a hyper-text should not move the insertion point to the click's location. The example below shows that within the same callback wrapped in tag_bind
, we can propagate or not the event to the Text
widget, simply by returning "break"
or another value.
try:
# Python2
import Tkinter as tk
except ImportError:
# Python3
import tkinter as tk
class TagBindWrapper:
def __init__(self, sequence, callback):
self.callback=callback
self.sequence=sequence
def __call__(self, event):
if "break" == self.callback(event):
global text
self.bind_id=text.bind(self.sequence, self.break_tag_bind)
return "break"
else:
return
def break_tag_bind(self, event):
global text
# text.after(100, text.unbind(self.sequence, self.bind_id))
text.after_idle(text.unbind, self.sequence, self.bind_id)
return "break"
def callback_normal(event):
print "normal text clicked"
return "break"
def callback_hyper(event):
print "hyper text clicked"
if event.state & 0x004: # ctrl modifier
return "break" # will not be passed on to text widget
else:
return # will be passed on to text widget
# setup Text widget
root=tk.Tk()
text = tk.Text(root)
text.pack()
text.tag_config("normal", foreground="black")
text.tag_config("hyper", foreground="blue")
text.tag_bind("hyper", "<Button-1>", TagBindWrapper("<Button-1>", callback_hyper))
text.tag_bind("normal", "<Button-1>", callback_normal)
# write some normal text and some hyper text
text.insert(tk.END, "normal text, ", "normal")
text.insert(tk.END, "hyper text (try normal-click and ctrl-click).", "hyper")
root.mainloop()
There is one simplification I couldn't find how to do: replace the wrapper call TagBindWrapper("<Button-1>", callback_hyper)
by TagBindWrapper(callback_hyper)
, i.e. get the information of the event 'sequence' string ("<Button-1>"
) simply from the event
object passed to __call__
. Is it possible?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With