Search code examples
pythontkinterpyautoguittkttkwidgets

Tkinter - TTK - Button change state after click


how can i change the state of a button made with TTK in Python after i clicked it ? I want to disable the button START after i press it once and re-enable it after pressing STOP. I want this because my code creates a new duplicated thread everytime i press Start ... so... i want to disable it... I used TTK instead of TK cause i saw it looks better but i cant seem to figure out how to use a global variable that just flips the state of that START button after i press it ...

Example :

frm2=ttk.Button(frm, text="Start", command=start_button).grid(column=0, row=3)

This is my entire script so you can run it yourself and see what i mean ...

from tkinter import *
from tkinter import ttk
import pyautogui
import keyboard
import time
import threading

gvar = False

def imagedetect():
    while gvar:
        if pyautogui.locateOnScreen('image.png', confidence = 0.9) != None:
            print("I can see it")
            time.sleep(1)
        else:
            print("I am unable to see it")
            time.sleep(1)
    #root.after(1000,imagedetect)

def start_button():
    global gvar
    gvar = True
    th_runner = threading.Thread(target=imagedetect, daemon=True)
    th_runner.start()

def on_start():
    global gvar
    gvar = True
def on_stop():
    global gvar
    gvar = False

def help():
    filewin = Toplevel(root, padx=50,pady=50)
    information = Label(filewin, text="App made by ..... you can close the app after starting it by pressing the key Q")
    information.pack()


root = Tk()
root.title("Rename me later")

menubar = Menu(root)
filemenu = Menu(menubar, tearoff=0)
filemenu.add_command(label="Help", command=help)
filemenu.add_separator()
menubar.add_cascade(label="Info", menu=filemenu)


frm = ttk.Frame(root, padding=30)
frm.pack()


ttk.Label(frm, text="App made by uknown").grid(column=1, row=0)

frm2=ttk.Label(frm, text="").grid(column=0, row=1)
frm2=ttk.Label(frm, text="").grid(column=1, row=1)
frm2=ttk.Label(frm, text="").grid(column=2, row=1)

frm2=ttk.Label(frm, text="").grid(column=0, row=2)
frm2=ttk.Label(frm, text="").grid(column=1, row=2)
frm2=ttk.Label(frm, text="").grid(column=2, row=2)

frm2=ttk.Button(frm, text="Start", command=start_button).grid(column=0, row=3)
frm2=ttk.Button(frm, text="Stop", command=on_stop).grid(column=1, row=3)
frm2=ttk.Button(frm, text="Quit", command=root.destroy).grid(column=2, row=3)
#root.destroy
root.config(menu=menubar)
#imagedetect()
root.mainloop()

Solution

  • Why did you use the same variable for multiple widgets? It doesn't work like that because a variable can only contain one value. If you want to change the button state later then you have to have separate variable names. Also you first have to create a button and then grid it. Not doing them in one line because you see grid returns none so that means you actually stored none in the variable instead of the widget class.

    To change the state of button you do this btn.config(state=DISABLED OR NORMAL)

    This should work:

    from tkinter import *
    from tkinter import ttk
    import pyautogui
    import keyboard
    import time
    import threading
    
    gvar = False
    
    def imagedetect():
        while gvar:
            if pyautogui.locateOnScreen('image.png', confidence = 0.9) != None:
                print("I can see it")
                time.sleep(1)
            else:
                print("I am unable to see it")
                time.sleep(1)
        #root.after(1000,imagedetect)
    
    def start_button():
        global gvar, btn
        btn.config(state=DISABLED)
        gvar = True
        th_runner = threading.Thread(target=imagedetect, daemon=True)
        th_runner.start()
    
    def on_start():
        global gvar
        gvar = True
    def on_stop():
        global gvar
        btn.config(state=NORMAL)
        gvar = False
    
    def help():
        filewin = Toplevel(root, padx=50,pady=50)
        information = Label(filewin, text="App made by ..... you can close the app after starting it by pressing the key Q")
        information.pack()
    
    
    root = Tk()
    root.title("Rename me later")
    
    menubar = Menu(root)
    filemenu = Menu(menubar, tearoff=0)
    filemenu.add_command(label="Help", command=help)
    filemenu.add_separator()
    menubar.add_cascade(label="Info", menu=filemenu)
    
    
    frm = ttk.Frame(root, padding=30)
    frm.pack()
    
    
    ttk.Label(frm, text="App made by uknown").grid(column=1, row=0)
    # You may need to change the names of these variables
    frm2=ttk.Label(frm, text="").grid(column=0, row=1)
    frm2=ttk.Label(frm, text="").grid(column=1, row=1)
    frm2=ttk.Label(frm, text="").grid(column=2, row=1)
    
    frm2=ttk.Label(frm, text="").grid(column=0, row=2)
    frm2=ttk.Label(frm, text="").grid(column=1, row=2)
    frm2=ttk.Label(frm, text="").grid(column=2, row=2)
    
    btn=ttk.Button(frm, text="Start", command=start_button) # Button is a class not a nonetype
    btn.grid(column=0, row=3)
    frm2=ttk.Button(frm, text="Stop", command=on_stop).grid(column=1, row=3)
    # This below is what 'frm2' will be equal to at the end so other widgets are lost so if need to use them later then you have to have seperate variables
    frm2=ttk.Button(frm, text="Quit", command=root.destroy).grid(column=2, row=3)
    
    #root.destroy
    root.config(menu=menubar)
    #imagedetect()
    root.mainloop()