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_bindreturn "break"

Textと同じイベント シーケンスでウィジェットをバインドしてウィジェットをハイジャックするというあなたのアイデアに従ってtag_bind、ソリューションを改善しました。これにより、Tk の他の bind+callback ペアの予想される動作をシミュレートできるようになりましたreturn "break"。アイデアは次のとおりです(以下の完全なソース):

  1. Wished の周りにラッパーを作成しますcallback。つまり、呼び出し可能なクラス インスタンスです。
  2. クラスインスタンスが呼び出されたら、実行callbackしてその結果を確認します。
    • 結果が の場合"break"、イベントの伝播を一時的にハイジャックします。空のコールバックを使用して、 にバインドされた同じイベントへbindのウィジェット。次に、アイドル時間の後、.Texttag_bindunbind
    • 結果が"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()

方法が見つからなかった簡略化が 1 つあります。ラッパー呼び出しTagBindWrapper("<Button-1>", callback_hyper)TagBindWrapper(callback_hyper)に置き換えます。つまり、イベント 'sequence' string () の情報を に渡され"<Button-1>"たオブジェクトから単純に取得します。出来ますか?event__call__

于 2013-04-16T13:55:41.043 に答える
2

さて、少しいじくり回した後実用的な解決策を見つけることができました。問題は、おそらくタグが 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 に答える