python「TKinter」のウィジェットを指定して画面を操る一例
日経BPさんの「みんなのラズパイコンテスト2020」でアイデア賞をいただきました! その作品で活用したpython「TKinter」について解説します。
やりたいこと
作品ではTKinterのウィジェットをラズパイに取り付けたセンサーで操作しましたが、このブログではTKinterに関わる以下の機能だけに絞って書きます。
- 好きな画像をウィンドウに配置する
- 配置した画像を隠すようにマス目上に正方形を敷き詰める
- 消すボタンを押すごとにその正方形がランダムで消えて画像が表れていく
- 戻すボタンを押すと正方形が敷き詰められて画像が隠れる
- 終わるボタンで終了する
こんな感じ。
実行環境
下記に載せるソースコードはwindowsでpython3.xをインストールし動作確認したものです。ラズパイで動作させる場合は、(アップデートさせていない限り)python2.xに対応させるべく
import tkinter import tkinter as tk
の部分を
import Tkinter import Tkinter as tk
とすればOKです("t"を大文字にする)。
また、ソースコードと同じディレクトリに「picture.gif」ファイルを置けば実行可能です。
ソースコード
import sys,random import tkinter import tkinter as tk from tkinter import messagebox root = tkinter.Tk() # 画面タイトル root.title(u"TKinterで配置したウィジェットを操作する") # 画面サイズの設定 root.geometry("720x720") # 使用する背景画像の読み込み picture = tk.PhotoImage(file="picture.gif") #キャンバスエリア canvas = tkinter.Canvas(root, width = 720, height = 540) canvas.create_image(0, 0, image=picture, tags="picture", anchor=tk.NW) #キャンバスバインド canvas.place(x=0, y=0) yoko = 8 tate = 6 size = 90 list = [[0 for i in range(yoko)] for j in range(tate)] #「戻す」ボタンが押されたら def retry(event): create_panel() #「消す」ボタンが押されたら def delete(event): delete_panel() #「終わる」ボタンが押されたら def exit(event): sys.exit() #パネル(正方形)をランダムで1つ消す def delete_panel(): rand_yoko = random.randint(0,yoko-1) rand_tate = random.randint(0,tate-1) total = 1 #すべてのパネルが消えているかチェック for i in range(yoko): for j in range(tate): total = total * list[j][i] #変数"total"が0ではなかったときすべてのパネルが消えている if total != 0: ret = messagebox.showinfo('ミッションコンプリート!', '「戻す」で再開するか「終わる」で終了ください') #変数"total"が0のとき1つ以上のパネルが残っている else: while True: #残っているパネルの位置を探す if list[rand_tate][rand_yoko] > 1: rand_yoko = random.randint(0,yoko-1) rand_tate = random.randint(0,tate-1) if list[rand_tate][rand_yoko] == 0: break canvas.delete("oval") canvas.delete("hide" + str(rand_yoko) + str(rand_tate)) list[rand_tate][rand_yoko] = 2 #パネル(正方形)を敷き詰めて初期状態に戻す def create_panel(): for i in range(yoko): for j in range(tate): #パネルを生成する(サイズ、位置、色、tagを指定して配置) canvas.create_rectangle(size*i, size*j, size*i+size, size*j+size, outline="white", fill="blue", tags="hide" + str(i) + str(j)) for i in range(yoko): for j in range(tate): list[j][i] = 0 # 「消す」ボタンを配置 button_draw = tkinter.Button(root, text=u'消す',width=15,height=2) button_draw.bind("<Button-1>",delete) button_draw.place(x=10,y=570) # 「戻す」ボタンを配置 button_draw = tkinter.Button(root, text=u'戻す',width=15,height=2) button_draw.bind("<Button-1>",retry) button_draw.place(x=160,y=570) # 「終わる」ボタンを配置 button_draw = tkinter.Button(root, text=u'終わる',width=15,height=2) button_draw.bind("<Button-1>",exit) button_draw.place(x=310,y=570) create_panel() root.mainloop()
解説
敷き詰めるパネルは2次元配列"list"と対応させ、例えば
- 左から3番目、上から1番目のパネルが消えたらlist[2][0]は"0"
- 左から7番目、上から2番目のパネルが残っていればlist[6][1]が"2"
としてパネルを管理できるようにします。pythonのウィジェットはtagsという引数で紐づけられるようになっています。
以下のコードで縦と横に対応したタグを付加しながらパネルを生成しています。
canvas.create_rectangle(size*i, size*j, size*i+size, size*j+size, outline="white", fill="blue", tags="hide" + str(i) + str(j))
以下のコードで縦と横に対応したタグを指定してパネルを削除しています。
canvas.delete("hide" + str(rand_yoko) + str(rand_tate))
上記を応用して例えばラズパイに接続したセンサーでウィジェットを生成したり削除したり操作するには、マルチスレッドを起動してそこでセンサー情報を監視、条件に一致したら操作する関数を呼び出します。こうして画面UIとリアルを(ネット接続を介さず)連動させたものを作れました。
おしまい。