Pythonで状態に合わせてボタンの見た目を変える(Tkinter) - Threading 追加 -
前回のGUIにStopボタンを実装した。
カウントアップする関数を別Threadにして、Stopボタンを押すことで停止のフラグを立てる、というもの。
※Print文で要所要所のThreadリストを出しているが、Threadがいつ死んでいるのかが結局わからなかった。
※あと、Thread.join()
は使い方がわからない。これがあるとプログラムが異常停止する…
greenhornprofessional.hatenablog.com
結果
チェックボックスにチェックを入れると、スタートボタンのみが有効になる。マウスオーバーで色が変わる。
スタートボタン押した直後、プログレスが100%になるまでの間、ストップボタンが有効になる。マウスオーバーで色が変わる。
ストップボタンを押すと、プログレスバー(カウントアップ)が停止し、ワーニング画面をだす。
※ワーニング画面を閉じた直後はまだThreadが残っている。
ワーニング画面を消すと、プログレスバーが0%に戻り、スタートボタンが有効になる。
※スタートボタン押すと、Thread = None
するようになっているが、この前にThreadが消えている。なぜ…
プログラム
# 23_tkinter_button2_001.py # python 3.8.1 # tkinter 8.6 # coding: utf-8 # import tkinter as tk from tkinter import ttk as ttk from tkinter import messagebox as tkm from tkinter import HORIZONTAL from time import sleep from datetime import datetime import threading #プログレスバーの進捗度を保存する変数 prbval = 0 stop_flag = False t = None #サイズ350x80で固定のウィンドウ作成 root = tk.Tk() root.title("Learning Buttons") root.geometry('350x80') root.resizable(width=False, height=False) #スタートボタンの内容 def start_button(): global stop_flag global t print(datetime.today(),":Start button clicked:", threading.enumerate()) t = None print(datetime.today(),":Thread killed :", threading.enumerate()) t = threading.Thread(target=run) stop_flag = False t.start() sleep(0.1) print(datetime.today(), ":Thread started :", threading.enumerate()) def run(): global prbval global stop_flag b1.configure(state=tk.DISABLED, bg='SystemButtonFace') b2.configure(state=tk.NORMAL, fg='Green4', bg='DarkSeaGreen1') chk1.configure(state=tk.DISABLED) try: while prbval <= 20: if stop_flag == True: raise ZeroDivisionError sleep(0.2) prbval = prbval + 1 prb.configure(value = prbval) prb.update() #この1行がないとプログレスが描画されない except ZeroDivisionError: tkm.showwarning("Warning", "Stop button clicked!") except: tkm.showerror("Error", "Unexpected error!!") else: tkm.showinfo("Info", "Complete") prbval = 0 prb.configure(value = prbval) b1.configure(state=tk.NORMAL, fg='Green4', bg='DarkSeaGreen1') b2.configure(state=tk.DISABLED, bg='SystemButtonFace') chk1.configure(state=tk.NORMAL) print(datetime.today(), ":run() finished :", threading.enumerate()) #ストップボタンの内容 def stop_button(): global stop_flag global t print(datetime.today(),":Stop button clicked :", threading.enumerate()) if t: stop_flag=True #スタートボタンにカーソルを重ねたときの挙動 def b1_mouseOver(event): if b1['state'] == 'normal': b1.configure(fg='Snow', bg='Green2') else: return #スタートボタンからカーソルを外したときの挙動 def b1_mouseOut(event): if b1['state'] == 'normal': b1.configure(fg='Green4', bg='DarkSeaGreen1') else: return #ストップボタンにカーソルを重ねたときの挙動 def b2_mouseOver(event): if b2['state'] == 'normal': b2.configure(fg='Snow', bg='Green2') else: return #ストップボタンからカーソルを外したときの挙動 def b2_mouseOut(event): if b2['state'] == 'normal': b2.configure(fg='Green4', bg='DarkSeaGreen1') else: return #チェックボックスの内容 def switch(bln): global t if bln == True: b1.configure(state=tk.NORMAL, fg='Green4', bg='DarkSeaGreen1') else: b1.configure(state=tk.DISABLED, bg='SystemButtonFace') b2.configure(state=tk.DISABLED, bg='SystemButtonFace') t = None print(datetime.today(), ":Button disabled :", threading.enumerate()) #チェックボックスの状態を表す変数の定義 bln1 = tk.BooleanVar() bln1.set(False) #チェックボックス作成 chk1 = tk.Checkbutton(root, variable=bln1, text="Activate Buttons", command=lambda: switch(bln1.get())) chk1.place(x=210, y=10) #プログレスバー用のラベル作成 lb1 = tk.Label(text="Progress Bar") lb1.place(x=10, y=12) #プログレスバー作成 prb = ttk.Progressbar(root, orient=HORIZONTAL, length=200, mode='determinate') prb.configure(maximum=20, value=prbval) prb.place(x=10, y=40) #スタートボタン作成、デフォルトがDISABLED b1 = tk.Button( root, text = "Start", width = 7, state = tk.DISABLED, disabledforeground = 'Gray45', command = start_button ) b1.bind('<Enter>', b1_mouseOver) b1.bind('<Leave>', b1_mouseOut) b1.place(x=215, y=38) #ストップボタン作成、デフォルトがDISABLED b2 = tk.Button( root, text = "Stop", width = 7, state = tk.DISABLED, disabledforeground = 'Gray45', command = stop_button ) b2.bind('<Enter>', b2_mouseOver) b2.bind('<Leave>', b2_mouseOut) b2.place(x=280, y=38) root.mainloop()