-1

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()
  • to change text use `label["text"] = "new text"`. If you want to add new text to existing text `label["text"] = label["text"] + "new text"` – furas Jun 19 '20 at 21:18
  • always put full error message (starting at word "Traceback") in question (not comment) as text (not screenshot). There are other useful information. – furas Jun 19 '20 at 21:19
  • BTW: maybe in `flag1` and `flag2` use `True/False` instead of `0/1` - it will be more readable. And you could use more readable names instead of `flag1`, `flag2` – furas Jun 19 '20 at 21:23
  • maybe at start create empty label and later only change text in this label - and don't create new labels. – furas Jun 19 '20 at 21:27
  • maybe when you run `printing_1` then set `flag1 = 1` and also `flag2 = 0` to stop `printing_2` – furas Jun 19 '20 at 21:28
  • BTW: in function you have to use `global flag1` to change external `flag1` - without `global` you create local variable `flag1` and it doesn't change external `flag1` – furas Jun 19 '20 at 21:32

1 Answers1

2

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()
furas
  • 134,197
  • 12
  • 106
  • 148
  • Thank you. Now it is working fine for the label. I apologize as I forgot to mention that it is requirement of my code, to display the items from the list when any key (except the enter key ) pressed from the keyboard. That's why I kept the `msvcrt.getwch()` function. However, if I'm using it, tkinter is crashing. Is there anyway to perform this? I'll make necessary update to the question now. – Mavis_Ackerman Jun 19 '20 at 23:49
  • I made changes to the code by taking your advice. I even removed the condition for enter here just to check if it changes to list in other option. However, it is same problem. I think the reason is `input1 = msvcrt.getwch()` statement. However to satisfy my requirement I needed it. Can you please give me any advice? Changes I made : `def update_2(): input1 = msvcrt.getwch() if running_2: label["text"] = random.choice(name_list) root.after(500, update_2)` same with update_1. – Mavis_Ackerman Jun 19 '20 at 23:58
  • Why do you use `getwch()` if `tkinter has `bind()` to work with keys, mouse and other events. – furas Jun 19 '20 at 23:59
  • if you want to update label only when you press key then you don't need `after` and `msvcrt` but only `bind` – furas Jun 20 '20 at 00:00
  • I don't know if I understand what you try to do with keys but I added some code in answer. – furas Jun 20 '20 at 00:18
  • Oh thank you. I was trying `bind` method with button and it wasnt working as I wanted. Thank you for your help. I got it now. – Mavis_Ackerman Jun 22 '20 at 16:42