Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Tkinter disable button while function is running

The following code works as expected unless the button is hit again while the function is being executed. I tried disabling the button in the function and re-enabling it when the function finishes but it is not working. What do I need to do to make this work?

import tkinter as tk
from tkinter import scrolledtext

root = tk.Tk()
root.title('Test Case')
root.geometry('850x300')
root.configure(background='ivory3')

label_fill = tk.Label(root, width = "80", height = "1", bg = 'ivory3')
label_fill.grid(row=1, columnspan=3)

textw = scrolledtext.ScrolledText(root, width=18, height=2)
textw.grid(column=0, row=2, sticky='nsew')
textw.tag_configure('tag-left', justify='left')
textw.config(background='light grey', foreground='green',
             font='arial 60 bold', wrap='word', relief='sunken', bd=5)


def func_ex(count=None):

    btn = tk.Button(state=tk.DISABLED)   

    if count is not None:
        if count <= 31:
            if (count % 3) == 1:
                txt = 'START'
                sleep = 2000               
            if (count % 3) == 2:
                txt = 'HOLD'
                sleep = 5000            
            if (count % 3) == 0:
                txt = 'END'
                sleep = 1000             
            if count == 31:
                txt = 'DONE'
                sleep = 1
            textw.delete('1.0', 'end')
            textw.insert('end', txt, 'tag-left')
            count += 1
            root.after(sleep, lambda: func_ex(count))
    else:
        func_ex(1)

    btn = tk.Button(state=tk.NORMAL)   


btn = tk.Button(root, text='Start Test Case', bg='light grey',
                width=18,font='arial 12', relief='raised', bd=5,
                command=func_ex)
btn = btn.grid(row=0, column=0)

root.mainloop()
like image 935
Dave Mathews Avatar asked Jul 13 '26 02:07

Dave Mathews


1 Answers

I have created a solution for you. The biggest difference I have made is by putting the application into a class. I find that it is much easier to put tkinter applications in a class to avoid issues with declaration order. You had several issues that I notated, but I could not find a way to create a working program without putting the application in a class. Feel free to ask any questions in the comments.

import tkinter as tk
from tkinter import scrolledtext


class AnApp:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title('Test Case')
        self.root.geometry('850x300')
        self.root.configure(background='ivory3')

        self.label_fill = tk.Label(self.root, width="80", height="1", bg='ivory3')
        self.label_fill.grid(row=1, columnspan=3)

        self.textw = scrolledtext.ScrolledText(self.root, width=18, height=2)
        self.textw.grid(column=0, row=2, sticky='nsew')
        self.textw.tag_configure('tag-left', justify='left')
        self.textw.config(background='light grey', foreground='green',
                          font='arial 60 bold', wrap='word', relief='sunken', bd=5)
        self.btn = tk.Button(self.root)

        self.btn.configure(text='Start Test Case',
                           bg='light grey', width=18,
                           font='arial 12', relief='raised',
                           bd=5, command=self.func_ex)
        self.btn.grid(row=0, column=0)  # grid returns none you had btn = btn.grid therefore your btn object became none

        self.count = 0

    def func_ex(self):
        self.count += 1
        txt = ""  # It is best practice to initialize variables...
        sleep = 0
        self.btn.configure(state=tk.DISABLED)  # you were also creating new btn objects here in your function
        if self.count <= 31:
            if (self.count % 3) == 1:
                txt = 'START'
                sleep = 2000
            if (self.count % 3) == 2:
                txt = 'HOLD'
                sleep = 5000
            if (self.count % 3) == 0:
                txt = 'END'
                sleep = 1000
            if self.count == 31:
                txt = 'DONE'
                sleep = 1
            self.textw.delete('1.0', 'end')
            self.textw.insert('end', txt, 'tag-left')
            self.root.after(sleep, self.func_ex)  # you can not pass a variable to a .after function even using lambda 
            # because it make a recursive function... basically a memory leak from my understanding.
        else:
            self.btn.configure(state=tk.NORMAL)
            self.count = 0

    def run(self):
        self.root.mainloop()


MyApp = AnApp()
MyApp.run()
like image 186
ShayneLoyd Avatar answered Jul 14 '26 20:07

ShayneLoyd



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!