2012-10-25 33 views
5

我目前正在編寫一個配色方案編輯器。對於該方案的預覽,我使用了一個文本小部件,在其中插入帶有相應顏色標籤(我通過編程生成)的文本。Python tkinter:停止文本小部件標籤中的事件傳播

我要的是以下行爲:

  • 點擊文本控件的任何地方沒有文字是:改變背景顏色
  • 點擊文本插入標籤:相應的前景色
  • 變化標籤

現在,這裏是我的問題:

當我點擊標記文本時,調用標籤的回調函數。到現在爲止還挺好。但是,文本控件的回調也被調用,儘管我在標記回調方法中返回「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。

回答

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() 

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

我仍然對更好的解決方案開放!

+1

標籤是特定於文本的項目,並且沒有標籤類別。它們絕對不是BaseWidget的子類。例如,對於Canvas特定的項目(例如弧和線)也是如此。很好的解決方法。 –

2

感謝您發佈此問題並提供解決方案。我無法計算我失去了多少小時,試圖修復由此行爲所產生的症狀。怪異Tk設計決定tag_bindreturn "break"不敏感。

按照你的想法用相同的事件序列tag_bind結合其劫持Text小部件,我有完善的解決方案,現在能夠模擬Tk的的其他綁定+回調對的預期return "break"行爲。我們的想法是以下(全下方源):

  1. 創建周圍的希望callback,包裝即一個可調用的類實例
  2. 當類實例被調用,運行callback並檢查其結果。
    • 如果結果爲"break",則暫時劫持事件傳播:bindText小部件將相同事件綁定到tag_bind,並帶有空回調。然後,在空閒時間後,unbind
    • 如果結果不是"break":什麼都不做,該事件將傳播到Text自動

這是一個完整的工作示例。我的具體問題是獲得某種超文本行爲:按住Ctrl鍵並單擊超文本不應將插入點移動到點擊的位置。下面的示例顯示,在tag_bind中包裝的同一個回調中,我們可以通過簡單地返回"break"或其他值來傳播或不傳播到Text小部件。

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() 

有一種簡單化我找不到怎麼辦:簡單地從傳遞給__call__event對象更換包裝呼叫TagBindWrapper("<Button-1>", callback_hyper)通過TagBindWrapper(callback_hyper),即獲得該事件的信息「序列」字符串("<Button-1>") 。可能嗎?

+0

作爲上述簡化的解決方法,可以編寫一個函數:'def tag_bind(textwidget,tagname,event_sequence,callback):textwidget.tag_add(tagname,event_sequence,TagBindWrapper(event_sequence,callback))' tag_bind(文本,「標記」,「<1>」,回調)'。只是避免重複參數。如果文本小部件被作爲TagBindWrapper類構造函數'__init__'的參數給出,那麼這個技巧也可以消除全局變量 –