web-dev-qa-db-ja.com

Python Tkinterリフレッシュキャンバス

こんにちは私はpythonに、次の辞書によってキャンバスに描画される正方形に関連する色のタプルを持っています:

_colour_mapping = {0: "red", 1: "green", 2: "blue" , 3:"purple"}
_

より具体的に言うと、たとえばタプルのノードは次のとおりです。

_((2, 3), (3, 3))
_

これは、4つの正方形を次のように描画する必要があることを意味します。

_blue square    purple square
purple square     purple square
_

次に、タプルの次のノードに応じて色を変更する必要があります

これを行うには、タプルを繰り返し、要素ごとにキャンバスに新しい長方形を描画してから、time.sleep()関数を呼び出して、ユーザーが前の状態との違いを確認できるようにします。私の問題は、最後のノードだけが正しくレンダリングされ、他のすべてのノードは表示されないことです。手伝って頂けますか?

これまでの私のコードは次のとおりです。

_self.parent.title("AlienTiles")
self.style = Style()
self.style.theme_use("default")

self.frame =  Frame(self, relief=RAISED, borderwidth=1)
self.frame.pack(fill=BOTH, expand=1)

self.canvas = Canvas(self.frame)
self.canvas.pack(fill=BOTH, expand=1)

self.pack(fill=BOTH, expand=1)


for i in range(len(path)) : #the Tuple is path

            state = path[i].state
            print state
            time.sleep(1)
            y_offset=10
            for x in state:
                start_x=40
                start_y=10
                i=1
                x_offset=0

                for y in x:

                    x0=(start_x*i)+x_offset
                    y0=(start_y*i)+y_offset
                    x1=x0+size
                    y1=y0+size
                    colour=colour_mapping[y]

                    print colour

                    self.canvas.create_rectangle(x0, y0, x1, y1, fill=colour)
                    x_offset=x_offset+size+10


                y_offset=y_offset+size+10
_

全体として、私は上記のアニメーションを作成しようとしています。正しく考えていないことや、ループごとにキャンバスを更新するものはありますか?

7
JmRag

キャンバスを更新する唯一の方法は、イベントループが「再描画」イベントを処理することです。ループでは、イベントループに更新の機会を与えることは決してないため、変更は表示されません。

簡単な解決策はself.canvas.update_idletasksを呼び出すことですが、これは単なるハックであり、適切な解決策ではありません。

アニメーションを実行する適切な方法は、イベントループを使用して反復を実行することです。これを行うには、実行する作業をキュー(この場合はアイドルイベントキュー)に配置します。 afterコマンドを使用して、このキューに物を置くことができます。

あなたがすべきことは、アニメーションを1回繰り返す関数を書くことです。基本的に、whileループ内のすべてを取得して、関数に移動します。次に、実行する作業がある限り、その関数が継続的に呼び出されるように調整します。その関数でafterを呼び出すか、アニメーションを制御する別の関数を使用できます。

大まかに言えば、ソリューションは次のようになります。

def do_one_frame(self, ...):
    # do whatever you need to draw one frame

    if (there_is_more_work_to_be_done):
        self.after(10, do_one_frame)

これにより、アニメーションの1つのフレームが描画され、描画する新しいフレームがあるかどうかが確認され、次のフレームが10ミリ秒で描画されるように調整されます。もちろん、アニメーションの速度を制御するために、その値を任意の値に設定できます。

このWebサイトには、この手法の実用的な例があります。たとえば、 https://stackoverflow.com/a/25431690/7432 を参照してください

13
Bryan Oakley