[英]Python tkinter: stopping event propagation in text widgets tags
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). 但是,也调用了文本小部件的回调,尽管我在标签回调方法中返回“break”(应该停止进一步的事件处理)。 How can I stop this? 我怎么能阻止这个?
To illustrate this specific problem, I wrote this working example (for Python 2 & 3): 为了说明这个具体问题,我写了这个工作示例(对于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. 编辑:尝试Python 2。
Okay, after
tkint
tinkering around a bit, I was able to figure out a working solution. 好吧,在
tkint稍微
修补一下后,我能够找到一个有效的解决方案。 I think the problem is that tags are probably not subclassed from BaseWidget. 我认为问题是标签可能不是BaseWidget的子类。
My workaround: 我的解决方法:
The workaround in code (sorry for using global
here, but I just modified my questions simple example...): 代码中的解决方法(抱歉在这里使用global
,但我只修改了我的问题简单示例...):
#!/usr/bin/env python
try:
from Tkinter import *
from tkMessageBox import showinfo
except ImportError:
from tkinter import *
from tkinter.messagebox import showinfo
tag_to_handle = ''
def on_click(event, widget_origin='?'):
global tag_to_handle
if tag_to_handle:
showinfo('Click', '"{}" clicked'.format(tag_to_handle))
tag_to_handle = ''
else:
showinfo('Click', '"{} " clicked'.format(widget_origin))
def on_tag_click(event, tag):
global tag_to_handle
tag_to_handle = tag
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_tag_click(e, w))
text.insert(CURRENT, tag_name + ' ', tag_name)
root.mainloop()
I hope this is helpful for people having the same problem. 我希望这对有同样问题的人有帮助。
I'm still open to nicer solutions of course! 当然,我仍然愿意接受更好的解决方案!
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"
. 奇怪Tk
是设计决定tag_bind
是insenstive到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 . 通过将其与tag_bind
相同的事件序列绑定来劫持Text
小部件,我已经改进了解决方案, 现在可以模拟Tk的其他绑定+回调对的预期return "break"
行为 。 The idea is the following (full source below): 这个想法如下(完整的来源如下):
callback
, ie a callable class instance 在希望的callback
周围创建一个包装器,即一个可调用的类实例 callback
and check its result. 调用类实例时,运行callback
并检查其结果。
"break"
, temporarily hijack the event propagation: bind
the Text
widget to the same event bound to tag_bind
, with an empty callback. 如果结果为"break"
,则暂时劫持事件传播: bind
Text
小部件绑定到绑定到tag_bind
的同一事件,并使用空回调。 Then, after an idle time, unbind
. 然后,在空闲时间之后, unbind
。 "break"
: do nothing, the event will propagate to Text
automatically 如果结果不是"break"
:什么都不做,事件将自动传播到Text
Here 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. 我的具体问题是获得某种超文本行为:按住Ctrl键单击超文本不应该将插入点移动到单击的位置。 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. 下面的示例显示,在tag_bind
中包含的同一回调中,我们可以将事件传播到Text
小部件,只需返回"break"
或其他值即可。
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)
, ie get the information of the event 'sequence' string ( "<Button-1>"
) simply from the event
object passed to __call__
. 有一个简化,我找不到怎么做:用TagBindWrapper(callback_hyper)
替换包装器调用TagBindWrapper("<Button-1>", callback_hyper)
TagBindWrapper(callback_hyper)
,即获取事件'sequence'字符串的信息( "<Button-1>"
)简单地从传递给__call__
的event
对象。 Is it possible? 可能吗?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.