Python独習!

習得したPython知識をペイフォワード

Pythonで状態に合わせてボタンの見た目を変える(Tkinter) - Threading 追加 -

前回のGUIにStopボタンを実装した。
カウントアップする関数を別Threadにして、Stopボタンを押すことで停止のフラグを立てる、というもの。
※Print文で要所要所のThreadリストを出しているが、Threadがいつ死んでいるのかが結局わからなかった。
※あと、Thread.join()は使い方がわからない。これがあるとプログラムが異常停止する…
greenhornprofessional.hatenablog.com

結果

チェックボックスにチェックを入れると、スタートボタンのみが有効になる。マウスオーバーで色が変わる。
f:id:greenhornprofessional:20200409231652p:plain

スタートボタン押した直後、プログレスが100%になるまでの間、ストップボタンが有効になる。マウスオーバーで色が変わる。
f:id:greenhornprofessional:20200409231713p:plain

ストップボタンを押すと、プログレスバー(カウントアップ)が停止し、ワーニング画面をだす。
※ワーニング画面を閉じた直後はまだThreadが残っている。
f:id:greenhornprofessional:20200409231731p:plain

ワーニング画面を消すと、プログレスバーが0%に戻り、スタートボタンが有効になる。
※スタートボタン押すと、Thread = Noneするようになっているが、この前にThreadが消えている。なぜ…
f:id:greenhornprofessional:20200409231750p:plain

プログラム

# 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()
/* -----codeの行番号----- */