Python tkinter: остановка распространения событий в тегах текстовых виджетов

Сейчас я пишу редактор цветовой схемы. Для предварительного просмотра схемы я использую текстовый виджет, куда я вставляю текст с соответствующими цветными тегами (которые я генерирую программно).

То, что я хочу, это следующее поведение:

щелкните в любом месте текстового виджета, где нет текста: измените цвет фонанажмите на текст, вставленный с тегом: изменить теги, соответствующие цвету переднего плана

Сейчас здесьмоя проблема:

Когда я нажимаю на текст с тегом, вызывается обратный вызов тега. Все идет нормально. Но затем вызывается обратный вызов текстового виджета, хотя я возвращаюсьперерыв" в тэгах метод обратного вызова (который должен остановить дальнейшую обработку событий).Как я могу это остановить?

Чтобы проиллюстрировать эту конкретную проблему, я написал этот рабочий пример (для 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('', 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, '',
        lambda e, w=tag_name: on_click(e, w))
    text.insert(CURRENT, tag_name + ' ', tag_name)
root.mainloop()

Любая помощь приветствуется!

Изменить: пробовал Python 2, а также.

Ответы на вопрос(2)

Спасибо за размещение этого вопроса и за решение. Я могу'не могу сосчитать, сколько часов я потерял, пытаясь исправить симптомы, вызванные этим поведением. странныйTk проектное решение, котороеtag_bind нечувствителен к.return "break"

После вашей идеи угнатьText виджет, связывая его с той же последовательностью событий, что иtag_bindЯ улучшил решение, котороепозволяет теперь моделировать ожидаемыйreturn "break" поведение Тк 's другие пары bind + callback, Идея заключается в следующем (полный источник ниже):

создать обертку вокруг желаемогоcallbackто есть вызываемый экземпляр классакогда вызывается экземпляр класса, запуститеcallback и проверь его результат.если результат"break"временно похитить распространение события:bind Text виджет к тому же событию, связанному с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", "", TagBindWrapper("", callback_hyper))
text.tag_bind("normal", "", 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("", callback_hyper) отTagBindWrapper(callback_hyper)получить информацию о событиипоследовательность' строка ("") просто изevent объект передан__call__, Является ли это возможным?

 armando.sano24 апр. 2013 г., 18:15
В качестве обходного пути для упрощения, упомянутого выше, можно написать функцию:def tag_bind(textwidget, tagname, event_sequence, callback): textwidget.tag_add(tagname, event_sequence, TagBindWrapper(event_sequence, callback)) просто тогда написатьtag_bind(text, "tag", "", callback), Просто избегает дублировать аргументы. Этот трюк также может устранить глобальную переменную, если текстовый виджет задан в качестве аргумента конструктора класса TagBindWrapper.__init__
Решение Вопроса

Хорошо после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('', 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, '',
        lambda e, w=tag_name: on_tag_click(e, w))
    text.insert(CURRENT, tag_name + ' ', tag_name)
root.mainloop()

Я надеюсь, что это полезно для людей, имеющих ту же проблему.

Я по-прежнему открыт для более хороших решений, конечно!

 Terry Jan Reedy30 янв. 2015 г., 07:52
Теги - это текстовые элементы, и класс Tag отсутствует. Они определенно не разделены на подклассы от BaseWidget. То же самое, например, для элементов Canvas, таких как дуги и линии. Хороший обходной путь.

Ваш ответ на вопрос