2012-10-25 40 views
5

Tôi hiện đang viết trình chỉnh sửa lược đồ màu. Để xem trước sơ đồ, tôi sử dụng một tiện ích văn bản, nơi tôi chèn văn bản với các thẻ màu tương ứng (mà tôi tạo ra theo chương trình).Python tkinter: dừng tuyên truyền sự kiện trong thẻ tiện ích văn bản

Những gì tôi muốn là hành vi sau đây:

  • nhấp chuột bất cứ nơi nào trên widget văn bản mà không có văn bản là: màu nền thay đổi
  • nhấp chuột trên văn bản chèn vào với một thẻ: thẻ thay đổi tương ứng với màu foreground

Bây giờ đây là vấn đề của tôi:

Khi tôi bấm vào một văn bản được gắn thẻ, các gọi lại của thẻ được gọi. Càng xa càng tốt. Nhưng sau đó, gọi lại của tiện ích văn bản được gọi là tốt, mặc dù tôi trở lại "phá vỡ" trong phương pháp gọi lại thẻ (mà nên dừng xử lý sự kiện tiếp theo). Tôi làm cách nào để ngăn chặn điều này?

Để minh họa cụ thể vấn đề này, tôi đã viết ví dụ làm việc này (đối với 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!

Chỉnh sửa: Đã thử Python 2.

Trả lời

2

Được rồi, sau khi tkint dò xung quanh một chút, tôi đã có thể tìm ra giải pháp làm việc. Tôi nghĩ rằng vấn đề là các thẻ có lẽ không được phân lớp từ BaseWidget.

workaround của tôi:

  • Thực hiện một callback riêng biệt cho các thẻ; thiết lập một biến có mà theo dõi trong đó thẻ được nhấp
  • Hãy để xử lý sự kiện của widget văn bản quyết định làm gì tùy thuộc vào nội dung của biến này

Cách giải quyết trong mã (xin lỗi vì sử dụng global đây , nhưng tôi vừa sửa đổi các câu hỏi ví dụ đơn giản của tôi ...):

#!/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() 

Tôi hy vọng điều này sẽ hữu ích cho những người gặp vấn đề tương tự.

Tôi vẫn mở cho các giải pháp đẹp hơn tất nhiên!

+1

Thẻ là các mục dành riêng cho văn bản và không có lớp Thẻ. Chúng chắc chắn không được phân lớp từ BaseWidget. Ví dụ, Ditto, cho các hạng mục cụ thể của Canvas như các cung và đường. Giải pháp tốt. –

2

Cảm ơn bạn đã đăng câu hỏi này và để cung cấp giải pháp. Tôi không thể đếm bao nhiêu giờ tôi đã mất cố gắng để sửa chữa các triệu chứng được tạo ra bởi hành vi này. Weird Tk quyết định thiết kế rằng tag_bind là không thích hợp để return "break".

Tiếp theo ý tưởng của bạn để chiếm quyền điều khiển widget Text bằng cách gắn nó với chuỗi sự kiện tương tự như tag_bind, tôi đã được cải thiện các giải pháp, trong đó phép tại để mô phỏng dự kiến ​​return "break" hành vi của Tk của bind + gọi lại cặp khác.Ý tưởng là như sau (đầy đủ nguồn dưới đây):

  1. tạo một wrapper quanh chúc callback, tức là một cá thể của lớp callable
  2. khi dụ lớp được gọi, chạy callback và kiểm tra kết quả của nó.
    • nếu kết quả là "break", tạm thời chiếm quyền điều khiển việc tuyên truyền sự kiện: bind các Text widget để cùng một sự kiện ràng buộc để tag_bind, với một callback rỗng. Sau đó, sau một thời gian nhàn rỗi, unbind.
    • nếu kết quả là không "break": không làm gì cả, sự kiện này sẽ tuyên truyền để Text tự động

Dưới đây là một ví dụ làm việc đầy đủ. Vấn đề cụ thể của tôi là nhận được một số loại văn bản quảng cáo siêu: nhấn ctrl trên siêu văn bản không được di chuyển điểm chèn vào vị trí của nhấp chuột. Ví dụ dưới đây cho thấy rằng trong cùng một cuộc gọi lại được gói trong tag_bind, chúng tôi có thể tuyên truyền hoặc không phải sự kiện cho tiện ích Text, chỉ cần trả lại "break" hoặc một giá trị khác.

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

Có một đơn giản tôi không thể tìm cách làm: thay lời gọi wrapper TagBindWrapper("<Button-1>", callback_hyper) bởi TagBindWrapper(callback_hyper), tức là lấy thông tin của sự kiện 'chuỗi' string ("<Button-1>") chỉ đơn giản là từ đối tượng event truyền cho __call__ . Có thể không?

+0

Như một giải pháp cho việc đơn giản hóa nêu trên, người ta có thể viết một hàm: 'def tag_bind (textwidget, tagname, event_sequence, callback): textwidget.tag_add (tagname, event_sequence, TagBindWrapper (event_sequence, callback))' để chỉ sau đó viết ' tag_bind (văn bản, "thẻ", "<1>", gọi lại) '. Chỉ tránh trùng lặp đối số. Bí quyết đó cũng có thể loại bỏ biến toàn cầu nếu tiện ích văn bản được đưa ra làm đối số của hàm tạo lớp TagBindWrapper '__init__' –

Các vấn đề liên quan