Search code examples
pythontkintermsvcrt

How to clear the tkinter label in loop and how to terminate the loop upon clicking another button


I'm a beginner in python Tkinter and I'm trying to print the characters from different lists upon clicking respective buttons. Once the button is clicked, the items from the list will keep on displaying whenever user presses any key from the keyboard. Upon pressing Enter key it should stop displaying from the current list. 1. The canvas is not cleaned up on the next iteration. The new label is created and placed at the top of the old label. I'm sharing a picture of the output I'm getting.

output_image

I know about the method w.delete('all') however, after using it my python is crashing. Can someone please suggest how can I modify it to erase completely?

  1. How to switch to the method of button 2 upon pressing button 2 when loop in button 1 is in progress. I've used or operator however my python is getting crashed. Right now, I able to exit only upon pressing the enter key. If I press another button, python is getting crashed. Can I please know the reason my python is crashing?

My code is:

import tkinter as tk
import msvcrt
import random


root= tk.Tk()
myCanvas = tk.Canvas(root, width = 500, height = 200)
myCanvas.pack()

w = tk.Canvas(root, width = 500, height = 200)
w.place(relx= 0.5, rely=0.1)



def printing_2():
    flag2=1
    name_list=['Mia Jennete Moi' , 'cara', 'juili', 'meera', 'ed']
    input1 = msvcrt.getwch()
    label2 = tk.Label(root, font=('Courier', 15))
    w.create_window(60, 40, window=label2)
    if flag1!=1 or input1!='\r':
        label2.config(text=random.choice(name_list))
        root.after(10, printing_2)

def printing_1():
    flag1=1
    name_list=['Rui Sagar' , 'Nana Kana', 'Mike', 'Bob', 'Drake']
    input1 = msvcrt.getwch()
    if flag2!=1 or input1!='\r':
        label2 = tk.Label(root, text=random.choice(name_list), font=('Courier', 15))
        w.create_window(60, 40, window=label2)
        root.after(10, printing_1)

flag1=0
flag2=0
B1 = tk.Button(root, text ="Option 1", font=('Courier', 12), command= printing_1)
myCanvas.create_window(40, 30, window=B1)
B2 = tk.Button(root, text ="Option 2", font=('Courier', 12), command= printing_2)
myCanvas.create_window(40, 80, window=B2)

root.mainloop()

Solution

  • You should create empty label at start and later only change text in existing label

     label2.config(text=random.choice(name_list))
    

    or

     label2["text"] = random.choice(name_list))
    

    When you press button then you could run function which change both values flag1 and flag2 (one set True and other set False) and after that run function which will run loop using after()

    And you have to use global to set external variables. Without global you create local variables.

    You could use more readable names running_1, running_2 instead of flag1, flag2 and use values True/False instead of 0/1

    And instead of msvcrt you can use root.bind("<Return>", stop) to run function stop() which will set running_1 = False and running_2 = False to stop all loop.s


    Minimal working code

    BTW: I use 500 in after() to slowdown it and see what values it displays.

    import tkinter as tk
    import random
    
    # --- functions
    
    def printing_1():
        global running_1
        global running_2
        global name_list
    
        running_1 = True
        running_2 = False  # stop other list
        name_list = ['Rui Sagar' , 'Nana Kana', 'Mike', 'Bob', 'Drake']
    
        print("start list 1")
        update_1()
    
    def update_1():
        if running_1:
            label["text"] = random.choice(name_list)
            root.after(500, update_1)
    
    def printing_2():
        global running_1
        global running_2
        global name_list
    
        running_1 = False  # stop other list
        running_2 = True
        name_list = ['Mia Jennete Moi' , 'cara', 'juili', 'meera', 'ed']
    
        print("start list 2")
        update_2()
    
    def update_2():
        if running_2:
            label["text"] = random.choice(name_list)
            root.after(500, update_2)
    
    def stop(event):
        global running_1
        global running_2
    
        running_1 = False
        running_2 = False
    
        print('stoped')
    
    # --- main ---
    
    running_1 = False
    running_2 = False
    name_list = None
    
    root = tk.Tk()
    
    root.bind('<Return>', stop)
    
    canvas = tk.Canvas(root, width=500, height=200)
    canvas.pack()
    
    w = tk.Canvas(root, width=500, height=200)
    w.place(relx=0.5, rely=0.1)
    
    label = tk.Label(root)
    w.create_window(60, 40, window=label)
    
    button1 = tk.Button(root, text="Option 1", command=printing_1)
    canvas.create_window(40, 30, window=button1)
    
    button2 = tk.Button(root, text="Option 2", command=printing_2)
    canvas.create_window(40, 80, window=button2)
    
    root.mainloop()
    

    EDIT:

    You can also reduce to use only one running and one function update_label().

    I check if running is True then I don't run new loop but it will automatically use already running loop but with new name_list

    import tkinter as tk
    import random
    
    # --- functions
    
    def printing_1():
        global name_list
        global running
    
        name_list = ['Rui Sagar' , 'Nana Kana', 'Mike', 'Bob', 'Drake']
    
        if not running:
            running = True
            update_label()
    
    def printing_2():
        global name_list
        global running
    
        name_list = ['Mia Jennete Moi' , 'cara', 'juili', 'meera', 'ed']
    
        if not running:
            running = True
            update_label()
    
    def update_label():
    
        if running:
            label["text"] = random.choice(name_list)
            root.after(500, update_label)
    
    def stop(event):
        global running
    
        running = False
    
        print('stoped')
    
    # --- main ---
    
    name_list = None
    running = False
    
    root = tk.Tk()
    
    root.bind('<Return>', stop)
    
    canvas = tk.Canvas(root, width=500, height=200)
    canvas.pack()
    
    w = tk.Canvas(root, width=500, height=200)
    w.place(relx=0.5, rely=0.1)
    
    label = tk.Label(root)
    w.create_window(60, 40, window=label)
    
    button1 = tk.Button(root, text="Option 1", command=printing_1)
    canvas.create_window(40, 30, window=button1)
    
    button2 = tk.Button(root, text="Option 2", command=printing_2)
    canvas.create_window(40, 80, window=button2)
    
    root.mainloop()
    

    EDIT:

    If you want to update label only when when you press key then you don't need msvcrt and after but only bind('<Key>', function_name)

    import tkinter as tk
    import random
    
    # --- functions
    
    def printing_1():
        """Assign new list and update label"""
        global name_list
    
        name_list = ['Rui Sagar' , 'Nana Kana', 'Mike', 'Bob', 'Drake']
    
        # set value at start
        update_label()
    
    def printing_2():
        """Assign new list and update label"""
        global name_list
    
        name_list = ['Mia Jennete Moi' , 'cara', 'juili', 'meera', 'ed']
    
        # set value at start
        update_label()
    
    def on_key(event):
        """Update label if list is assigned to `name_list`
           and pressed key is different then `Return`"""
        if name_list and event.keysym != 'Return':
            update_label()
    
    def update_label():
        label["text"] = random.choice(name_list)
    
    # --- main ---
    
    name_list = None
    
    root = tk.Tk()
    root.bind('<Key>', on_key) # run function when pressed any key
    
    canvas = tk.Canvas(root, width=500, height=200)
    canvas.pack()
    
    w = tk.Canvas(root, width=500, height=200)
    w.place(relx=0.5, rely=0.1)
    
    label = tk.Label(root)
    w.create_window(60, 40, window=label)
    
    button1 = tk.Button(root, text="Option 1", command=printing_1)
    canvas.create_window(40, 30, window=button1)
    
    button2 = tk.Button(root, text="Option 2", command=printing_2)
    canvas.create_window(40, 80, window=button2)
    
    root.mainloop()