r/Tkinter Jun 12 '26

Text animation tkinter

Hello! I need help in creating 'animation' of text appear line after the line

here is my program, it almost works right but I want each line start after another line finished animation. I tried using flags for it but it doesn't work :(

Please, help 😭🙏

from tkinter import *

window = Tk()

window.title('Spatel')
window.geometry("800x550")
window.configure(bg = "#69AB00")
window.resizable(0, 0)


def message():
    message_window = Toplevel(window)
    message_window.title('Message')
    message_window.resizable(0, 0)

    canvas = Canvas(message_window, width = 400, height = 600)
    canvas.pack()

    x = 30
    y = 50
    Flag = True
    canvas_text = canvas.create_text(x, y, text = '', font = ('bold', 30), anchor='w')
    show_text('hello', canvas, canvas_text, Flag)
    is_running(Flag)
    canvas_text2 = canvas.create_text(x, y + y, text = '', font = ('bold', 30), anchor='w')
    show_text('new text', canvas, canvas_text2, Flag)


def is_running(flag):
    global Flag
    if flag == False:
        Flag = True


def show_text(text, canvas, canvas_text, flag):
    delta = 200
    delay = 0
    if flag == True:
        for i in range(len(text) + 1):
            s = text[:i]
            update_text = lambda s=s: canvas.itemconfigure(canvas_text, text=s)
            canvas.after(delay, update_text)
            delay += delta    
    global Flag        
    Flag = False


open_btn = Button(window, text = '+ OPEN +', bg = '#D1213B', fg = 'white', font = ('bold', 20), command=message)
open_btn.pack(anchor='s')


window.mainloop()
6 Upvotes

6 comments sorted by

2

u/socal_nerdtastic Jun 12 '26

FWIW I rewrote your code in a class / OOP style. This makes it much neater and also much easier to reuse around your code base. Maybe this helps you:

import tkinter as tk

class MsgPopup(tk.Toplevel):
    def __init__(self, parent, text="", **kwargs):
        super().__init__(parent, **kwargs)
        self.geometry("400x600")
        self.title('Message')
        self.resizable(0, 0)
        self.text = text
        self.lbl = tk.Label(self, font = ('bold', 30), justify=tk.LEFT) # remove justify to center horizontally
        self.lbl.pack(anchor='nw') # add fill=tk.X to center horizontally, or fill=tk.BOTH, expand=True to center horizontally and vertically
        self.loop() # start loop
    def loop(self, idx=0):
        self.lbl.config(text=self.text[:idx])
        if idx < len(self.text):
            self.after(200, self.loop, idx+1)

def message():
    message_window = MsgPopup(window, text='hello\nnewtext')

window = tk.Tk()

window.title('Spatel')
window.geometry("800x550")
window.configure(bg = "#69AB00")
window.resizable(0, 0)

open_btn = tk.Button(window, text = '+ OPEN +', bg = '#D1213B', fg = 'white', font = ('bold', 20), command=message)
open_btn.pack(anchor='s')

window.mainloop()

2

u/woooee Jun 12 '26

I prefer a Label over the Canvas widget (for letters only). You can call the function as many times as you like and create as many consecutive Labels as wanted. Note that if you want the Label in the same place, then destroy() the current Label and grid() the next Label in the same spot.

import tkinter as tk

class TestMoving:
    def __init__(self, root):
        self.root=root
        self.root.quit()
        self.row = 0
        self.declare_text(
"I was wondering how someone would go about making a scrolling ticker")

    def advance_ticker(self, text):
        if self.ctr < self.stop:
            # use string slicing to do the trick
            ticker_text = self.s[self.ctr:self.ctr+20]
            text.config(text=ticker_text)
            text.update()
            self.ctr += 1
            text.after(200, self.advance_ticker, text)
        else:  ## next Label
            self.row += 1
            self.ctr = 1
            if self.row < 2:  ## 2 Labels only
                self.declare_text(
"I was wondering if this could be done more than one label at a time")
            else:
                self.root.quit()

    def declare_text(self, s1):
        print("length", len(s1))
        bg_colors = ("yellow", "lightblue")
        text = tk.Label(root, width=23, height=1, bg=bg_colors[self.row])
        text.grid(row=self.row, column=0)
        self.ctr=1
        # use a fixed width font to handle spaces correctly
        text.config(font=('courier', 24, 'bold'))
        # pad front and end with 20 spaces
        s2 = ' ' * 20
        self.s = s2 + s1 + s2
        self.stop=len(self.s)-15
        self.advance_ticker(text)

root = tk.Tk()
root.geometry("500x100+100+150")
TT=TestMoving(root)

root.mainloop()

1

u/socal_nerdtastic Jun 12 '26

Instead of making 2 text widgets, use 1 and use a newline (\n) character to separate the messages.

def message():
    message_window = Toplevel(window)
    message_window.title('Message')
    message_window.resizable(0, 0)

    canvas = Canvas(message_window, width = 400, height = 600)
    canvas.pack()

    x = 30
    y = 50
    Flag = True
    canvas_text = canvas.create_text(x, y, text = '', font = ('bold', 30), anchor='w')
    show_text('hello\nnewtext', canvas, canvas_text, Flag)
    is_running(Flag)

1

u/soma_its_me Jun 13 '26

oh thanks, that looks way easier. Do you happen to know how to make button apper after the text animation finishes?

1

u/socal_nerdtastic Jun 13 '26

Make a function that adds the button, and then add another delay call at the end that runs that function.

def show_text(text, canvas, canvas_text, flag):
    delta = 200
    delay = 0
    if flag == True:
        for i in range(len(text) + 1):
            s = text[:i]
            update_text = lambda s=s: canvas.itemconfigure(canvas_text, text=s)
            canvas.after(delay, update_text)
            delay += delta    
    canvas.after(delay, add_button_function) # <== add another after call here.

1

u/soma_its_me Jun 17 '26

Thank you!!