Search code examples
pythonpython-3.xtkintertkinter-label

Update tkinter label and use entry to choose parameter


I made a little script to practice the use of tkinter. I want the program to open a window and show a label. The label should show a random number between 0 and 100 once I press the button. Also I want the label to refresh every second and show another random number.

from tkinter import *
import random
import time
root = Tk()

def getrandint():
    result = random.randint(0, 100)
    return result


def go():
    lab2['text'] = 'Number: ' + str(getrandint())
    lab2.pack()

    root.geometry('300x200')
    root.mainloop()
    time.sleep(1)
    go()


lab1 = Label(root, text='Type in number')
lab2 = Label(root, text='Number:')
#ent = Entry(root, width=20)
#number = ent.get()
b = Button(root, text='Go', command=go())

b.pack()
lab1.pack()
lab2.pack()
#ent.pack()

This is how far I got. It opens a window and shows a random number, but isn't refreshing the number. The Button isn't even showing. Also, when I close the window, Python 3.8 shows me this error message:

Traceback (most recent call last):
  File "C:/Users/chris/Desktop/WeatherLinkPy/testing.py", line 102, in <module>
    b = Button(root, text='Go', command=go())
  File "C:/Users/chris/Desktop/WeatherLinkPy/testing.py", line 95, in go
    go()
  File "C:/Users/chris/Desktop/WeatherLinkPy/testing.py", line 89, in go
    lab2['text'] = 'Number: ' + str(getrandint())
  File "C:\Users\chris\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 1660, in __setitem__
    self.configure({key: value})
  File "C:\Users\chris\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 1649, in configure
    return self._configure('configure', cnf, kw)
  File "C:\Users\chris\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 1639, in _configure
    self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: invalid command name ".!label2"

Finally, is there a way to change the second parameter of random.randint(0, b) with an entry and a button at the beginning?


Solution

    • Lets start with the basic mistakes, your placement of mainloop() decides what all widgets are displayed(on a well written code). In this case, you want to put mainloop() at the end. In you case this even work because you were using () with the button that has the function which contains mainloop()(a little tricky to explain through words :P).

    • Now the next is, you shouldn't be calling the function using () for your buttons, as it will start the function automatically and will not wait for the button to be pressed:

    b = Button(root, text='Go', command=go)
    
    • Next would be to fix your entry widget and event driven programming. You shouldn't be taking the input from the user at the beginning of your code as it will be empty at the beginning. You should take the input and store it inside a variable after an event(a function or so) is triggered, which means your getrandint() will be:
    def getrandint():
        try: # To ignore if non integers are entered into the entry widget
            result = random.randint(0, int(ent.get())) # Get the text from entry widget
            return result
        except TypeError: # Ignore the error
            pass
    
    • Next thing is to avoid the usage of time.sleep() as it will lag the GUI. So you should instead use root.after(ms,func) which will call the func after a specified ms:
    def go():
        lab2['text'] = 'Number: ' + str(getrandint())
        root.after(1000,go) # Repeat the function after 1 second
    

    So your final code would be:

    from tkinter import *
    import random
    
    root = Tk()
    root.geometry('300x200')
    
    def getrandint():
        try:
            result = random.randint(0, int(ent.get()))
            return result
        except TypeError:
            pass
    
    def go():
        lab2['text'] = 'Number: ' + str(getrandint())
        root.after(1000,go)
    
    lab1 = Label(root, text='Type in number')
    lab2 = Label(root, text='Number:')
    ent = Entry(root, width=20)
    b = Button(root, text='Go', command=go)
    
    b.pack()
    lab1.pack()
    lab2.pack()
    ent.pack()
    
    root.mainloop() # At the end of the code
    

    Also note:

    You can completely get rid of your getrandint() by changing your go() into:

    def go():
        try: # If not an integer is entered into entry widget
            lab2['text'] = 'Number: ' + str(random.randint(0,int(ent.get())))
            root.after(1000,go)
        except ValueError: # Ignore the error
            pass
    

    With all this being said, your error happens due to the way your code flows. Practice more often keeping these in mind, and you shall never see these errors again ;)