簡體   English   中英

Python tkinter:在文本小部件標簽中停止事件傳播

[英]Python tkinter: stopping event propagation in text widgets tags

我正在寫一個配色方案編輯器。 對於方案的預覽,我使用文本小部件,其中我插入帶有相應顏色標簽的文本(我以編程方式生成)。

我想要的是以下行為:

  • 單擊文本小部件上沒有文本的任何位置:更改背景顏色
  • 單擊插入標簽的文本:更改標簽對應的前景色

現在這是我的問題:

當我單擊標記文本時,將調用標記的回調。 到現在為止還挺好。 但是,也調用了文本小部件的回調,盡管我在標簽回調方法中返回“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。

好吧,在 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()

我希望這對有同樣問題的人有幫助。

當然,我仍然願意接受更好的解決方案!

感謝您發布此問題並提供解決方案。 我無法計算我試圖修復這種行為造成的症狀的時間。 奇怪Tk是設計決定tag_bind是insenstive到return "break"

通過將其與tag_bind相同的事件序列綁定來劫持Text小部件,我已經改進了解決方案, 現在可以模擬Tk的其他綁定+回調對的預期return "break"行為 這個想法如下(完整的來源如下):

  1. 在希望的callback周圍創建一個包裝器,即一個可調用的類實例
  2. 調用類實例時,運行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", "<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(callback_hyper)替換包裝器調用TagBindWrapper("<Button-1>", callback_hyper) TagBindWrapper(callback_hyper) ,即獲取事件'sequence'字符串的信息( "<Button-1>" )簡單地從傳遞給__call__event對象。 可能嗎?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM