7

我目前正在编写一个配色方案编辑器。对于方案的预览,我使用了一个文本小部件,在其中插入带有相应颜色标签的文本(我以编程方式生成)。

我想要的是以下行为:

  • 单击文本小部件上没有文本的任意位置:更改背景颜色
  • 单击插入带有标签的文本:更改标签对应的前景色

现在这是我的问题:

当我单击标记文本时,将调用标记的回调。到现在为止还挺好。但是,文本小部件的回调也被调用,尽管我在标签回调方法中返回“break”(这应该停止进一步的事件处理)。我怎样才能阻止这个?

为了说明这个具体问题,我编写了这个工作示例(适用于 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()

任何帮助表示赞赏!

编辑:也尝试过 Python 2。

4

2 回答 2

3

感谢您发布此问题并提供解决方案。我无法计算为了修复这种行为造成的症状而损失了多少小时。对. Tk_ tag_bind_return "break"

Text按照您通过将小部件与相同的事件序列绑定来劫持小部件的想法tag_bind,我改进了解决方案,现在可以模拟return "break"Tk 的其他绑定+回调对的预期行为。这个想法如下(下面的完整来源):

  1. 围绕希望创建一个包装器callback,即一个可调用的类实例
  2. 当调用类实例时,运行callback并检查其结果。
    • 如果结果是"break",则暂时劫持事件传播:bindText部件与绑定到 的相同事件tag_bind,回调为空。然后,在一段空闲时间之后,unbind.
    • 如果结果不是"break":什么都不做,事件将Text自动传播到

这是一个完整的工作示例。我的具体问题是获得某种超文本行为:ctrl 单击超文本不应将插入点移动到单击位置。下面的示例显示,在包含在 中的同一个回调中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()

有一种简化我找不到怎么做:用 替换包装器调用TagBindWrapper("<Button-1>", callback_hyper)TagBindWrapper(callback_hyper)即简单地从传递给"<Button-1>"的对象获取事件“序列”字符串 ()的信息。是否可以?event__call__

于 2013-04-16T13:55:41.043 回答
2

好的,在tkint修改了一下之后,我找到了一个可行的解决方案。我认为问题在于标签可能不是 BaseWidget 的子类。

我的解决方法:

  • 对标签进行单独的回调;在那里设置一个变量来跟踪哪个标签被点击
  • 让文本小部件的事件处理程序根据此变量的内容决定要做什么

代码中的解决方法(抱歉在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()

我希望这对遇到同样问题的人有所帮助。

当然,我仍然愿意接受更好的解决方案!

于 2012-10-25T20:10:17.067 回答