読者です 読者をやめる 読者になる 読者になる

神野さんに言われました。

AIの勉強をしています @sesenosannko

Tkinterで関数内でcanvasを変更するときの問題について

Tkinter

こんにちは。

Tkinterでボタンを押した時にcanvas等を変更したいときは、buttonのcommandに与える関数内で操作するのが定石だと思うのですが、アプリを作っている中で期待する挙動をしてくれないことがありました。
Tk自体やTkinterとTkの関係を全く知らないので、もしかしたら当たり前のことを言っているのかもしれないですが、

import time

def change(canvas):
    canvas.create_text(100, 100, text='スタート')
    
    time.sleep(5)
    
    canvas.delete('all')
    canvas.create_text(100, 100, text='ストップ')

このような関数に対して

button = tkinter.Button(root, text="5秒", command=lambda:change(canvas))
button.pack()

ありきたりなボタンを作ります。
期待する挙動は、ボタンを押したら「スタート」と表示され、5秒後に「ストップ」と表示されることですね。

しかし、実際には何も表示されないまま5秒が経過して「ストップ」と表示されます。
この問題についての情報は見つけられなかったのですが、どうやら関数が終了するまでcanvasは更新されないようです。

ここで、バインドを使用することにしました。
バインドはクリックや特定のキーの入力などに対してアクションを起こすことができる機能らしいです。
でクリックした時、でクリックをやめた時のアクションを指定することができます。
つまり、自然にボタンをクリックしてもらえれば、単純に2つの独立したアクションをほぼ同時に起こすことができます。

def before(canvas):
    canvas.create_text(100, 100, text='スタート')

def after(canvas):
    time.sleep(5)
    
    canvas.delete('all')
    canvas.create_text(100, 100, text='ストップ')

このように二つの関数を準備して、

button = Button(root, text="5秒")
button.bind('<Button-1>', lambda e:change(canvas))
button.bind('<ButtonRelease-1>', lambda e:after(canvas))
button.pack()

このように指定すれば、ボタンをクリックしたときに「スタート」と表示され、直ぐにボタンを離してくれれば5秒後に「ストップ」と表示されます。
bindでは指定した関数にevent objectが与えられ、クリックした位置などの情報を扱うことができますが、今回は必要無いのでlamba式に「e」として与えて、それ以降は使いません。