web-dev-qa-db-ja.com

tkinterイベントバインディングの削除と変更

イベントの処理を停止したり、呼び出される関数を切り替えたりするにはどうすればよいですか?

改訂コード:

from Tkinter import *

class GUI:
    def __init__(self,root):
        Window = Frame(root)
        self.DrawArea = Canvas(Window)
        self.DrawArea.pack()
        Window.pack()

        self.DrawArea.bind("<Button 1>",self.starttracking)

    def updatetracking(self,event):
        print event.x,event.y

    def finishtracking(self,event):
        self.DrawArea.bind("<Button 1>",self.starttracking)
        self.DrawArea.unbind("<Motion>")

    def starttracking(self,event):
        print event.x,event.y
        self.DrawArea.bind("<Motion>",self.updatetracking)
        self.DrawArea.bind("<Button 1>",self.finishtracking)



if __name__ == '__main__':
    root = Tk()
    App = GUI(root)
    root.mainloop()
19
Symon

イベントの新しい関数を使用して、bind()を再度呼び出すだけです。 bind()の3番目のパラメータaddを使用していないため、これはすでに存在するものを上書きするだけです。デフォルトでは、このパラメーターは_''_ですが、_"+"_も受け入れます。これにより、そのイベントによって既にトリガーされたコールバックにコールバックが追加されます。

ただし、そのオプションの引数の使用を開始する場合は、unbind()関数を使用して個々のコールバックを削除する必要があります。 bind()を呼び出すと、funcidが返されます。このfuncidを2番目のパラメーターとしてunbind()に渡すことができます。

例:

_self.btn_funcid = self.DrawArea.bind("<Button 1>", self.my_button_callback, "+")

# Then some time later, to remove just the 'my_button_callback':
self.DrawArea.unbind("<Button 1>", self.btn_funcid)

# But if you want to remove all of the callbacks for the event:
self.DrawArea.unbind("<Button 1>")
_
32
Bryan

ブライアンによって提供された答えは一般的にうまく機能しますが、アルクラが強調しているように、そうではないかもしれません。スタックされたコールバックを正しくアンバインドできないという問題が発生した場合は、公式ソースを変更してください-それでも同じであれば! -解決策かもしれません。

それでも問題が解決しない場合は、2セントに従ってください。unbind()メソッドをオーバーライドしてください。直接編集しないでください。

このように、実際には、ワークステーションの公式ソースコードを手動で変更する必要はありません(したがって、パッケージ管理を壊したり、次のパッケージ更新時に問題を再導入したり、別のクライアントで同じ問題が発生したりします...) :

import tkinter as tk


class PatchedCanvas(tk.Canvas):
    def unbind(self, sequence, funcid=None):
        '''
        See:
            http://stackoverflow.com/questions/6433369/
            deleting-and-changing-a-tkinter-event-binding-in-python
        '''

        if not funcid:
            self.tk.call('bind', self._w, sequence, '')
            return
        func_callbacks = self.tk.call(
            'bind', self._w, sequence, None).split('\n')
        new_callbacks = [
            l for l in func_callbacks if l[6:6 + len(funcid)] != funcid]
        self.tk.call('bind', self._w, sequence, '\n'.join(new_callbacks))
        self.deletecommand(funcid)

次に、失敗したウィジェット(私の例ではCanvasを使用)をインスタンス化する代わりに、次のようにします

myCanvas = tk.Canvas(...)

公式ソースが更新および修正される場合にのみ、更新が必要なパッチが適用されたバージョンからインスタンス化するだけです。

myCanvas = PatchedCanvas(...)

Unbindメソッドは現在Miscクラスで定義されており、そこからBaseWidgetが継承し、その結果、Widget、TopLevel、Button、...

5
Alberto Vassena

私にとって、単一のコールバックのバインドを解除することは機能していませんでしたが、解決策を見つけました。

これは古い質問だと思いますが、私のように同じ問題に直面したときにこの質問を見つけた人にとって、これは私がそれを機能させるためにしたことです。

ソースファイルTkinter.pyを開き、Miscクラスのunbindメソッドを検索する必要があります(Eclipseを使用している場合は、F3キーを押してファイルの場所とこの関数が定義されている行を簡単に知ることができます。カーソルは、コード内の.unbind関数呼び出しの上にあります)。

それを見つけると、次のようなものが表示されます。

def unbind(self, sequence, funcid=None):
        """Unbind for this widget for event SEQUENCE  the
        function identified with FUNCID."""
        self.tk.call('bind', self._w, sequence, '')
        if funcid:
            self.deletecommand(funcid)

次のように変更する必要があります。

def unbind(self, sequence, funcid=None):
        """Unbind for this widget for event SEQUENCE  the
        function identified with FUNCID."""
    if not funcid:
        self.tk.call('bind', self._w, sequence, '')
        return
    func_callbacks = self.tk.call('bind', self._w, sequence, None).split('\n')
    new_callbacks = [l for l in func_callbacks if l[6:6 + len(funcid)] != funcid]
    self.tk.call('bind', self._w, sequence, '\n'.join(new_callbacks))
    self.deletecommand(funcid)

それでうまくいくはずです!

5
arcra