Create 3D Text Animations In Python Tkinter

How To Create an Animated Text in Python Using Tkinter

How to Create 3D Text Animation In Python Tkinter


In this Python Tutorial we will see How to Create an animated text effects using Python and Tkinter library.
The text appears with a growth animation, cycles through vibrant colors, and maintains subtle movement patterns while displaying layered shadow effects for depth and impact.

What We Are Gonna Use In This Project:

- Python Programming Language.
- Tkinter (GUI).
- VS Editor.






Project Source Code:




import tkinter as tk
import math
import time
from tkinter import font

class TextAnimation(tk.Canvas):
"""
A smooth animated text effect that creates a glowing, colorful text
with shadow layers and gentle rotation.
"""
def __init__(self, master, text, **kwargs):
super().__init__(master, **kwargs)
self.text = text
# Animation state variables
self.scale = 0.1 # Text starts small and grows
self.is_growing = True # Flag to control growth phase
self.rotation_angle = 0 # Current rotation angle
self.color_time = 0 # Time variable for color cycling
# Animation settings
self.grow_speed = 0.02 # How fast text grows (0.01 = slow, 0.05 = fast)
self.rotation_speed = 0.1 # How fast text rotates
self.color_speed = 0.03 # How fast colors change
self.shadow_layers = 30 # Number of shadow/glow layers
# Colors for the gradient effect (RGB values)
self.colors = [
(255, 0, 128), # Hot pink
(255, 102, 0), # Orange
(255, 215, 0), # Gold
(0, 255, 255) # Cyan
]
# List of texts to cycle through when clicked
self.text_options = [
"FIGHT!",
"POWER!",
"EPIC!",
"AWESOME!",
"VICTORY!",
"LEGEND!",
"SUPREME!",
"ULTIMATE!"
]
self.current_text_index = 0
# Set up click event to change text
self.bind("<Button-1>", self.on_click)
# Start the animation loop
self.animate()

def animate(self):
"""
Main animation loop - called repeatedly to update the animation
"""
# Update animation values
self.update_animation_values()
# Clear the canvas and redraw everything
self.delete("all")
self.draw_animated_text()
# Schedule the next frame (20ms delay)
self.after(20, self.animate)
def update_animation_values(self):
"""
Update all the animation variables for smooth movement
"""
# Handle the growing phase when text first appears
if self.is_growing:
self.scale += self.grow_speed
if self.scale >= 1.0:
self.scale = 1.0
self.is_growing = False
# Update rotation and color cycling (always happening)
self.rotation_angle += self.rotation_speed
self.color_time += self.color_speed

def draw_animated_text(self):
"""
Draw the text with all effects: shadows, colors, and rotation
"""
# Get canvas dimensions for centering
width = self.winfo_width()
height = self.winfo_height()
center_x = width // 2
center_y = height // 2
# Calculate font size based on window size and current scale
base_font_size = min(width, height) // 6
current_font_size = int(base_font_size * self.scale)
text_font = font.Font(family="Impact", size=current_font_size, weight="bold")
# Draw shadow layers first (back to front)
self.draw_shadow_layers(center_x, center_y, text_font)
# Draw the main text on top
self.draw_main_text(center_x, center_y, text_font)


def draw_shadow_layers(self, x, y, text_font):
"""
Draw multiple shadow layers
"""
for layer in range(self.shadow_layers, 0, -1):
# Calculate how far back this shadow layer is (0.0 to 1.0)
depth = layer / self.shadow_layers
# Get color for this shadow layer
shadow_color = self.get_gradient_color(depth + self.color_time)
# Make shadow
color_hex = self.rgb_to_hex(shadow_color)
# Calculate shadow offset with gentle wave motion
wave = math.sin(self.rotation_angle + layer * 0.1)
offset_x = layer * 2 + wave * 1.5
offset_y = layer * 2 + wave * 1.5
# Draw this shadow layer
self.create_text(
x + offset_x, y + offset_y,
text=self.text,
font=text_font,
fill=color_hex,
anchor="center"
)


def draw_main_text(self, x, y, text_font):
"""
Draw the main text with bright colors and subtle rotation
"""
# Get the main color
main_color = self.get_gradient_color(self.color_time)
color_hex = self.rgb_to_hex(main_color)
# Add gentle swaying motion (only after growing is complete)
sway = 0
if not self.is_growing:
sway = math.sin(self.rotation_angle * 0.5) * 3
# Draw the main text
self.create_text(
x + sway, y,
text=self.text,
font=text_font,
fill=color_hex,
anchor="center"
)
# Add a bright highlight on top
highlight_color = self.rgb_to_hex((255, 255, 255))
self.create_text(
x + sway - 1, y - 1,
text=self.text,
font=text_font,
fill=highlight_color,
anchor="center"
)


def get_gradient_color(self, position):
"""
Get a color from the gradient based on position (0.0 to 1.0)
Creates smooth color transitions between the defined colors
"""
# Keep position between 0 and 1
position = position % 1.0
# Find which two colors to blend
color_sections = len(self.colors) - 1
scaled_position = position * color_sections
color_index = int(scaled_position)
blend_amount = scaled_position - color_index
# Get the two colors to blend
color1 = self.colors[color_index]
color2 = self.colors[(color_index + 1) % len(self.colors)]
# Blend the colors smoothly
r = int(color1[0] + (color2[0] - color1[0]) * blend_amount)
g = int(color1[1] + (color2[1] - color1[1]) * blend_amount)
b = int(color1[2] + (color2[2] - color1[2]) * blend_amount)
return (r, g, b)


def on_click(self, event):
"""
Handle mouse clicks to change the text
"""
# Move to the next text in the list
self.current_text_index = (self.current_text_index + 1) % len(self.text_options)
self.text = self.text_options[self.current_text_index]
# Reset the growing animation
self.scale = 0.3 # Start a bit bigger for text changes
self.is_growing = True
# Add a little color boost when text changes
self.color_time += 0.5


def rgb_to_hex(self, rgb_color):
"""
Convert RGB color values to hex format for tkinter
rgb_color: tuple of (red, green, blue) values (0-255)
"""
r, g, b = rgb_color
return f"#{r:02x}{g:02x}{b:02x}"




class MainWindow(tk.Tk):
"""
Main application window that contains the text animation
"""
def __init__(self):
super().__init__()
# Window setup
self.title("Smooth Text Animation")
self.geometry("800x400")
self.configure(bg="#1a1a1a") # Dark background
# Create the animated text
self.animation = TextAnimation(
self,
text="FIGHT!",
bg="#1a1a1a", # Match window background
highlightthickness=0 # Remove border
)
# Make the animation fill the entire window
self.animation.pack(fill=tk.BOTH, expand=True)


# Run the application
if __name__ == "__main__":
app = MainWindow()
app.mainloop()




The Final Result:

Python Text Animation With 3D Effects Using Tkinter - 1

Python Text Animation With 3D Effects Using Tkinter - 2

Python Text Animation With 3D Effects Using Tkinter - 3

Python Text Animation With 3D Effects Using Tkinter - 4

Python Text Animation With 3D Effects Using Tkinter - 5

Python Text Animation With 3D Effects Using Tkinter - 6

Python Text Animation With 3D Effects Using Tkinter - 7

Python Text Animation With 3D Effects Using Tkinter - 8









Python Tkinter View Switcher - List And Grid View

How To Create a List And Grid View Switcher in Python Tkinter

How To Create a List And Grid View Switcher in Python Tkinter


In this Python Tutorial we will see How to Create a Dynamic Layout Switching between list and grid views in Python and Tkinter.

What We Are Gonna Use In This Project:

- Python Programming Language.
- Tkinter (GUI).
- VS Editor.






Project Source Code:




import tkinter as tk
from tkinter import ttk
import random

class ViewSwitcher(tk.Tk):
"""
This is our main application class window.
"""
def __init__(self):
"""
This method runs automatically when we create a new instance of our class.
"""
# Call the parent class (tk.Tk) setup method
super().__init__()
# Set up the basic window properties
self.title("Modern View Switcher") # Text shown in title bar
self.geometry("800x600") # Window size: width x height
self.configure(bg="#f0f0f0") # Background color (light gray)
# Create a style object to make our widgets look more modern
self.style = ttk.Style()
self.style.theme_use('clam') # Use the 'clam' theme
# Configure how different widget types should look
# Frames have gray background
self.style.configure('TFrame', background='#f0f0f0')
self.style.configure('TButton', font=('Arial', 10)) # Buttons use Arial font

# Create some fake data to display in our app
self.items = self._create_sample_data(20) # Create 20 sample items
# Keep track of which view mode we're currently using
self.view_mode = "list" # Start with list view (could be "list" or "grid")
# Build the user interface
self._setup_ui()
def _create_sample_data(self, count):
"""
This method creates fake data for our app to display.
Parameters:
count (int): How many items to create
Returns:
list:
"""
items = [] # Start with an empty list
# Define some categories to randomly choose from
categories = ["Technology", "Fashion", "Food", "Travel", "Sports"]
# Create the specified number of items
for i in range(1, count + 1):
# Each item is a dictionary (like a mini-database record)
item = {
"id": i, # Unique number for this item
"title": f"Item {i}", # Title (f-string puts the number in)
"category": random.choice(categories), # Pick a random category
"description": f"This is the description for item {i}."+
"It contains some details about the item.",
# Random color in hex format
"color": "#{:06x}".format(random.randint(0, 0xFFFFFF))
}
items.append(item) # Add this item to our list
return items # Return the complete list
def _setup_ui(self):
"""
This method creates all the visual elements (widgets) for our app.
"""
# Create the main container that holds everything
self.main_frame = ttk.Frame(self)
# Pack it to fill the entire window with some padding around the edges
self.main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
# Create the header section (top part with title and buttons)
self.header_frame = ttk.Frame(self.main_frame)
# Pack it at the top, make it stretch horizontally, add space below it
self.header_frame.pack(fill=tk.X, pady=(0, 20))
# Create the title label
title_label = ttk.Label(
self.header_frame, # Put it in the header frame
text="View Switcher", # The text to display
font=("Arial", 18, "bold"), # Font: Arial, size 18, bold
background="#f0f0f0" # Background color
)
title_label.pack(side=tk.LEFT) # Put it on the left side
# Create a frame to hold our view switcher buttons
switcher_frame = ttk.Frame(self.header_frame)
switcher_frame.pack(side=tk.RIGHT) # Put it on the right side
# Create the "List View" button
self.list_btn = tk.Button(
switcher_frame, # Parent frame
text="☰ List", # Button text with icon
width=8, # Button width
font=("Arial", 10), # Font settings
# Background: blue if active, gray if not
bg="#4a86e8" if self.view_mode == "list" else "#e0e0e0",
# Text: white if active, black if not
fg="white" if self.view_mode == "list" else "black",
relief=tk.FLAT, # Flat style (no 3D effect)
borderwidth=0, # No border
cursor="hand2", # Hand cursor when hovering
command=lambda: self.switch_view("list") # What to do when clicked
)
# Pack on left with small right margin
self.list_btn.pack(side=tk.LEFT, padx=(0, 2))
# Create the "Grid View" button (similar to list button but for grid view)
self.grid_btn = tk.Button(
switcher_frame,
text="▦ Grid", # Button text with grid icon
width=8,
font=("Arial", 10),
# Opposite colors from list button
bg="#e0e0e0" if self.view_mode == "list" else "#4a86e8",
fg="black" if self.view_mode == "list" else "white",
relief=tk.FLAT,
borderwidth=0,
cursor="hand2",
command=lambda: self.switch_view("grid") # Switch to grid view when clicked
)
self.grid_btn.pack(side=tk.LEFT)
# Create a frame to hold our scrollable content area
self.canvas_frame = ttk.Frame(self.main_frame)
self.canvas_frame.pack(fill=tk.BOTH, expand=True) # Fill remaining space
# Create a canvas (like a drawing surface) that can be scrolled
self.canvas = tk.Canvas(
self.canvas_frame,
bg="#f0f0f0", # Background color
highlightthickness=0 # Remove the border highlight
)
# Create a scrollbar for when content is too tall to fit
self.scrollbar = ttk.Scrollbar(
self.canvas_frame,
orient=tk.VERTICAL, # Vertical scrollbar
command=self.canvas.yview # Connect it to canvas vertical scrolling
)
# Place the canvas and scrollbar in the frame
# Canvas takes most space
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # Scrollbar on right edge
# Connect the canvas to the scrollbar
self.canvas.configure(yscrollcommand=self.scrollbar.set)
# Create a frame inside the canvas to hold our actual content
self.content_frame = ttk.Frame(self.canvas)
# Put the content frame inside the canvas
self.canvas_window = self.canvas.create_window((0, 0),
window=self.content_frame, anchor="nw")
# Set up event handlers
# When frame size changes
self.content_frame.bind("<Configure>", self._on_frame_configure)
# When canvas size changes
self.canvas.bind("<Configure>", self._on_canvas_configure)
# When mouse wheel is used
self.bind_all("<MouseWheel>", self._on_mousewheel)
# Show the initial view (list view by default)
self.render_view()
def _on_frame_configure(self, event):
"""
This method runs when the content frame changes size.
We need to update the scrollable area to match the new size.
"""
# Tell the canvas how big the scrollable area should be
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def _on_canvas_configure(self, event):
"""
This method runs when the canvas changes size (like when window is resized).
We need to make sure the content frame matches the canvas width.
"""
width = event.width # Get the new width
# Update the content frame to match the canvas width
self.canvas.itemconfig(self.canvas_window, width=width)
def _on_mousewheel(self, event):
"""
This method runs when the user scrolls with their mouse wheel.
It makes the content scroll up or down.
"""
# Scroll the canvas up or down based on wheel direction
# event.delta is positive for up, negative for down
self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")
def render_view(self):
"""
This method displays the content in the current view mode.
It decides whether to show list view or grid view.
"""
# First, remove any existing content (clear the screen)
for widget in self.content_frame.winfo_children():
widget.destroy() # Delete each widget
# Now show the content in the appropriate format
if self.view_mode == "list":
self._render_list_view() # Show items in a vertical list
else:
self._render_grid_view() # Show items in a grid layout

def _render_list_view(self):
"""
This method creates the list view - items shown one below another.
"""
# Go through each item in our data
for item in self.items:
# Create a frame to hold this one item
item_frame = ttk.Frame(self.content_frame)
# Pack it horizontally with vertical spacing
item_frame.pack(fill=tk.X, pady=5)
# Create a colored bar on the left side of each item
color_indicator = tk.Frame(
item_frame,
bg=item["color"], # Use the item's color
width=5 # Make it 5 pixels wide
)
# Put it on the left, full height
color_indicator.pack(side=tk.LEFT, fill=tk.Y)
# Create a container for the text content
content = ttk.Frame(item_frame)
content.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=10, pady=8)
# Create a container for the title and category (they go on the same line)
header = ttk.Frame(content)
header.pack(fill=tk.X)
# Create the title label
title = ttk.Label(
header,
text=item["title"], # The item's title
font=("Arial", 12, "bold"), # Bold font for emphasis
background="#f0f0f0"
)
title.pack(side=tk.LEFT) # Put it on the left side of the header
# Create the category label (like a colored tag)
category = tk.Label(
header,
text=item["category"], # The item's category
font=("Arial", 10), # Smaller font than title
bg=item["color"], # Background color matches item color
fg="white", # White text for contrast
padx=6, # Padding on left and right
pady=2, # Padding on top and bottom
borderwidth=0 # No border
)
category.pack(side=tk.RIGHT) # Put it on the right side of the header
# Create the description label
description = ttk.Label(
content,
text=item["description"], # The item's description
wraplength=650, # Wrap text if it gets too long
background="#f0f0f0"
)
description.pack(anchor=tk.W, pady=(5, 0)) # Left-align with top margin


def _render_grid_view(self):
"""
This method creates the grid view - items shown in a grid pattern.
"""
# Set up the grid: we want 3 columns
columns = 3
# Configure each column to expand equally
for i in range(columns):
self.content_frame.columnconfigure(i, weight=1)
# Place each item in the grid
for index, item in enumerate(self.items):
# Calculate which row and column this item should go in
row = index // columns # Integer division gives us the row
col = index % columns # Remainder gives us the column
# Create a card for this item
card = tk.Frame(
self.content_frame,
bg="white", # White background
relief=tk.RAISED, # Slightly raised appearance
bd=0, # No border width
highlightbackground=item["color"], # Colored border
highlightthickness=2 # Border thickness
)
# Place the card in the grid
card.grid(row=row, column=col, padx=10, pady=10, sticky="nsew")
# Create the title label inside the card
title = tk.Label(
card,
text=item["title"], # Item title
font=("Arial", 12, "bold"), # Bold font
bg="white", # White background
anchor="w" # Left-align the text
)
title.pack(fill=tk.X, padx=12, pady=(12, 5)) # Pack with padding
# Create the category label (colored tag)
category = tk.Label(
card,
text=item["category"], # Category text
font=("Arial", 10), # Smaller font
bg=item["color"], # Colored background
fg="white", # White text
padx=6, # Horizontal padding
pady=2 # Vertical padding
)
category.pack(anchor=tk.W, padx=12, pady=(0, 8)) # Left-align with padding
# Create the description (shortened for grid view to save space)
# Take first 50 characters and add "..."
description_text = item["description"][:50] + "..."
description = tk.Label(
card,
text=description_text, # Shortened description
wraplength=200, # Wrap text at 200 pixels
justify=tk.LEFT, # Left-justify the text
bg="white", # White background
anchor="w" # Left-align
)
description.pack(fill=tk.X, padx=12, pady=(0, 12)) # Pack with padding
def switch_view(self, mode):
"""
This method switches between list and grid views.
It's called when the user clicks the List or Grid buttons.
Parameters:
mode (str): Either "list" or "grid" - which view to switch to
"""
# If we're already in the requested mode, don't do anything
if mode == self.view_mode:
return
# Update our current view mode
self.view_mode = mode
# Update the button colors to show which one is active
if mode == "list":
# List is active: make list button blue, grid button gray
self.list_btn.configure(bg="#4a86e8", fg="white")
self.grid_btn.configure(bg="#e0e0e0", fg="black")
else:
# Grid is active: make grid button blue, list button gray
self.list_btn.configure(bg="#e0e0e0", fg="black")
self.grid_btn.configure(bg="#4a86e8", fg="white")
# Re-draw the content in the new view mode
self.render_view()




if __name__ == "__main__":
app = ViewSwitcher()
app.mainloop()




The Final Result:

Python Tkinter Grid View

Python Tkinter List View