3

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()
1
  • Why didn't code it in a OOP manner? Commented Dec 7, 2019 at 19:36

2 Answers 2

2

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()
Sign up to request clarification or add additional context in comments.

2 Comments

That works fairly good. The button is disabled while the function is running and appears to be re-enabled after the function completes. However, pressing the button after the function competes doesn't do anything. I would expect it to call the function again.
This is probably because count is never reset to 0 so it is always greater than 31. I will fix this in the above code.
1

After looking at the comments in ShayneLoyd's code above I was able to find the problem with my code. As stated, I was creating a new Button in the function. I fixed this by making btn global and using .configure to update the state of the button. The following code fixes the problem in my original code (button could be pressed multiple times while the function was running) and in ShayneLoyd's code (pressing the button after the function completes the first time does nothing)

Here's the working code

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):

    global btn
    btn.configure(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))
        elif count == 32:
             btn.configure(state=tk.NORMAL)
    else:
        func_ex(1)


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

root.mainloop()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.