6

文字がウィジェットに入力されるたびに、Textそのウィジェットの内容を取得し、特定の数からその長さを引きたいと思います (基本的には「x 文字が残っています」という取引)。

しかし、StringVar()常に 1 つのイベントが遅れています。これは、文字が Text ウィジェットに入力される前にイベントが処理されるためです。これは、フィールドに 3 文字があり、4 番目を入力すると、StringVarが更新されますが、まだ 3 文字の長さであり、5 番目の文字を入力すると 4 に更新されることを意味します。

2つを一致させる方法はありますか?

ここにいくつかのコードがあります。不要な部分を削除しました。

def __init__(self, master):
    self.char_count = StringVar()
    self.char_count.set("140 chars left")

    self.post_tweet = Text(self.master)
    self.post_tweet.bind("<Key>", self.count)
    self.post_tweet.grid(...)

    self.char_count = Label(self.master, textvariable=self.foo)
    self.char_count.grid(...)

def count(self):
    self.x = len(self.post_tweet.get(1.0, END))
    self.char_count.set(str(140 - self.x))
4

2 に答える 2

4

簡単な解決策は、クラス バインディングの後に新しい bindtag を追加することです。そうすれば、バインディングの前にクラス バインディングが起動します。テキスト ウィジェットによってバインドされた後、Tkinter テキスト ウィジェットでセルフ イベントをバインドする方法についての質問に対するこの回答を参照してください。たとえば。この回答では、テキスト ウィジェットではなくエントリ ウィジェットを使用していますが、bindtags の概念はこれら 2 つのウィジェット間で同じです。適切な場所ではなく、必ず使用してください。TextEntry

もう 1 つの解決策は、デフォルトのバインドが KeyPress で発生するため、KeyRelease でバインドすることです。

bindtags を使用してそれを行う方法を示す例を次に示します。

import Tkinter as tk

class Example(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)

        self.post_tweet = tk.Text(self)
        bindtags = list(self.post_tweet.bindtags())
        bindtags.insert(2, "custom") # index 1 is where most default bindings live
        self.post_tweet.bindtags(tuple(bindtags))

        self.post_tweet.bind_class("custom", "<Key>", self.count)
        self.post_tweet.grid()

        self.char_count = tk.Label(self)
        self.char_count.grid()

    def count(self, event):
        current = len(self.post_tweet.get("1.0", "end-1c"))
        remaining = 140-current
        self.char_count.configure(text="%s characters remaining" % remaining)

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(side="top", fill="both", expand=True)
    root.mainloop()
于 2013-04-02T20:52:07.090 に答える
3

Tk のほとんどのイベントと同様に、ハンドラーは、イベントが組み込みバインディングによって処理された後ではなく、処理される前に<Key>起動されます。これにより、たとえば、通常の処理が行われないようにしたり、処理内容を変更したりできます。

ただし、これは、まだ更新されていないため、新しい値にアクセスできないことを意味します ( を介してStringVar、または を呼び出すだけで)。entry.get()


を使用している場合は、「変更済み」フラグが変更された後にText発生する仮想イベントがあります。<<Modified>>そのフラグを別の目的で使用していないと仮定すると (たとえば、テキスト エディターでは、「保存ボタンを有効にする」という意味で使用したい場合があります)、それを使用して、まさに必要なことを行うことができます。

def count(self, event=None):
    if not self.post_tweet.edit_modified():
        return
    self.post_tweet.edit_modified(False)
    self.x = len(self.post_tweet.get(1.0, END))
    self.char_count.set(str(140 - self.x))

# ...

self.post_tweet.bind("<<Modified>>", self.count)

通常、このようなものが必要な場合は、Entryではなく が必要ですText。これは、これを行うためのはるかに優れた方法である検証を提供します。Tkinter の基本を超えたすべてのものと同様に、Tcl/Tk ドキュメントを読まずにこれを理解する方法はありません (これが、Tkinter ドキュメントがそれらにリンクしている理由です)。実際、Tk のドキュメントでさえ検証についてはあまり詳しく説明していません。しかし、これがどのように機能するかです:

def count(self, new_text):
    self.x = len(new_text)
    self.char_count.set(str(140 - self.x))
    return True

# ...

self.vcmd = self.master.register(self.count)
self.post_tweet = Edit(self.master, validate='key',
                       validatecommand=(self.vcmd, '%P'))

validatecommand、関数に渡す 0 個以上の引数のリストを取ることができます。引数は、%P許可した場合にエントリが持つ新しい値を取得します。詳細についてVALIDATIONは、エントリのマンページを参照してください。

入力を拒否したい場合 (たとえば、誰かが 140 文字を超えて入力するのを実際にブロックしたい場合) はFalseTrue.


ところで、Tk wikiを調べて、 ActiveStateで Tkinter レシピを検索することをお勧めします。誰かがラッパーTextEntry持っていて、これらのソリューション (または他のソリューション) を機能させるために必要なすべての余分なものを隠しているので、適切なcountメソッドを作成するだけでよいのです。スタイルの検証を追加するラッパーさえあるかもしれませんTextEntry

これを行う方法は他にもいくつかありますが、いずれも欠点があります。

a を追加して、ウィジェットにアタッチされtraceた へのすべての書き込みをフックします。StringVarこれは、変数への書き込みによって発生します。初めて検証に使用しようとすると、無限再帰ループの問題が発生することを保証します。その後、将来、他のより微妙な問題に遭遇することになります。通常の解決策は、番兵フラグを作成することです。このフラグは、ハンドラーに入るたびにチェックして、再帰的に実行していないことを確認し、再帰イベントをトリガーできる何かを実行しているときに設定します。(edit_modified上記の例では、フラグを に設定した人を無視することができたので、これは必要ありませんでした。フラグを に設定したFalseだけFalseなので、無限再帰の危険はありません。)

<Key>仮想イベントから新しい文字 (または複数文字の文字列) を取得できます。しかし、その後、あなたはそれで何をしますか?どこに追加されるのか、どの文字が上書きされるのかなどを知る必要があります。シミュレートするすべての作業を行わない場合、Entryまたはさらに悪いことに、Text自分で編集する場合、これは単にやってlen(entry.get()) + 1ます。

于 2013-04-02T19:13:18.123 に答える