0

I'm designing a GUI to decrease the time which my colleagues spend for reporting tests. But I stuck in the animation of opening screen. I mixed the code that I found on the internet which writes the chosen text letter one by one with a second for loop to enlarge the text but the first loop worked only for the last word of the list. Also, I tried the same code with a while loop both with giving count or just writing "True" but they didn't solve my problem either.

I want to see the chosen texts (now, only a few words but later on I will write my tests) written on the screen one by one and letter by letter. How can I solve this?

My sketch code is as follows:

import tkinter as tk
import random

root = tk.Tk()
root.configure(bg="white")

Words=["Canvas", "Import", "Index", "Random", "Tkinter"]


canvas=tk.Canvas(root, width=400, height=375, bg="white")
canvas.pack()

for word in Words:
    x=random.randint(0, 250)
    y=random.randint(0, 225)
    canvas_text=canvas.create_text(x, y, text=word)
    delta=500 
    delay=0
    for i in range(len(word)+1):
        s=word[:i]
        update_text=lambda s=s: canvas.itemconfigure(canvas_text, text=s)
        canvas.after(delay, update_text)
        delay+=delta
        x=random.randint(0, 250)
        y=random.randint(0, 225)
        
root.mainloop()

Image description

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
mdiramali
  • 15
  • 4
  • Can you more clearly describe the behaviour that you expect to see? Not sure what this little animation has to do with reporting tests, seems like a distraction from the real problem. – scotty3785 May 04 '21 at 10:18
  • I just want to see the names of the tests randomly one by one on the screen when program is not working because I don't want an empty canvas. Now all the names came at the same time. – mdiramali May 04 '21 at 11:18

1 Answers1

0

There are several issues in your code:

  1. With canvas_text = canvas.create_text(x, y, text=word) you immediately write the full word, it would make more sense to start with an empty string, canvas_text = canvas.create_text(x, y, text=""), then add the letters one by one. Additionally, you can add the option anchor="w" so that adding the letters will not move the word (otherwise the default anchor is the center of the object).

  2. delay is reset to 0 for each word so the letters will appear one by one but at the same time for all words.

  3. update_text is defined as lambda s=s: canvas.itemconfigure(canvas_text, text=s) and therefore canvas_text refers to the last created canvas object (see What do (lambda) function closures capture?). As a consquence, only the last word is animated. To avoid this, you can use an additional argument in the function: lambda s=s, c=canvas_text: canvas.itemconfigure(c, text=s)

  4. A few additional remarks:

    • delta=500 can be moved out of the for loop

    • The x = ... and y = ... in the nested for loop are useless and can be removed

    • update_text=lambda s=s: canvas.itemconfigure(canvas_text, text=s): lambda is typically used to avoid having to give the function a name, so one typically either defines a named function with def or uses a lambda directly, e.g.

      canvas.after(delay, lambda c=canvas_text, s=word[:i]: canvas.itemconfigure(c, text=s))
      

Here is the full code:

import tkinter as tk
import random

root = tk.Tk()
root.configure(bg="white")

Words = ["Canvas", "Import", "Index", "Random", "Tkinter"]

canvas = tk.Canvas(root, width=400, height=375, bg="white")
canvas.pack()
delay = 0
delta = 500
for word in Words:
    x = random.randint(0, 250)
    y = random.randint(0, 225)
    canvas_text = canvas.create_text(x, y, text="", anchor="w")

    for i in range(len(word)+1):
        canvas.after(delay, lambda c=canvas_text, s=word[:i]: canvas.itemconfigure(c, text=s))
        delay += delta

root.mainloop()
j_4321
  • 15,431
  • 3
  • 34
  • 61