Skip to content

Instantly share code, notes, and snippets.

@Tajcore
Created April 11, 2024 00:01
Show Gist options
  • Save Tajcore/82d643caf6b886bddb49316187a205db to your computer and use it in GitHub Desktop.
Save Tajcore/82d643caf6b886bddb49316187a205db to your computer and use it in GitHub Desktop.
main.py
import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
import json
class MainApplication(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.title("Hulu Clone")
self.geometry("800x600")
# Create a main container frame
self.container = tk.Frame(self, bg="#101622")
self.container.pack(side="top", fill="both", expand=True)
# Row 0 will be for the nav bar, with no weight (it shouldn't expand)
self.container.grid_rowconfigure(0, weight=0)
# Row 1 will be for the content frames, with a weight (it will expand to fill space)
self.container.grid_rowconfigure(1, weight=1)
self.container.grid_columnconfigure(0, weight=1)
# Initialize and stack frames in row 1
self.frames = {}
for F in (HomePage, MoviesPage, TVPage):
frame = F(self.container)
self.frames[F] = frame
frame.grid(row=1, column=0, sticky="nsew")
# The nav bar is created and placed in row 0
self.content = self.load_content()
self.movies = [item for item in self.content if item["category"] == "Movie"]
self.tv_shows = [item for item in self.content if item["category"] == "TV Show"]
self.create_nav_bar()
self.show_frame(HomePage)
def load_content(self):
try:
with open("assets/content.json", "r") as file: # Renamed to content.json
return json.load(file)
except Exception as e:
print(f"Error loading content: {e}")
return []
def create_nav_bar(self):
style = ttk.Style()
style.theme_use("alt")
style.configure("TButton", background="#101622", foreground="white")
# Top navigation bar in the container frame
# Search icon button
nav_bar_color = "#101622"
top_nav_bar = tk.Frame(self.container, bg=nav_bar_color)
top_nav_bar.grid(row=0, column=0, sticky="ew", padx=(5, 5), pady=(20, 20))
search_icon_image = Image.open("assets/icons/search_icon.png").resize(
(20, 20), Image.Resampling.LANCZOS
) # Adjust path as needed
self.search_icon_photo = ImageTk.PhotoImage(search_icon_image)
self.search_button = ttk.Button(
top_nav_bar,
image=self.search_icon_photo,
style="TButton",
command=self.toggle_search,
)
self.search_button.pack(side=tk.RIGHT, padx=10)
# Hidden search entry
self.search_var = tk.StringVar()
self.search_entry = tk.Entry(
top_nav_bar, textvariable=self.search_var, width=30
)
# Hulu logo
logo_image = Image.open("assets/logo/hulu.png").resize(
(40, 20), Image.Resampling.LANCZOS
)
logo_photo = ImageTk.PhotoImage(logo_image)
logo_label = tk.Label(top_nav_bar, image=logo_photo, bg="#1a1a1a")
logo_label.image = logo_photo # Keep a reference
logo_label.pack(side=tk.LEFT, padx=10)
# Container frame for the navigation buttons, which will be centered
self.nav_buttons_frame = tk.Frame(top_nav_bar, bg="#101622")
self.nav_buttons_frame.pack(side=tk.LEFT, expand=True)
# Navigation buttons inside the container frame
self.home_button = ttk.Button(
self.nav_buttons_frame,
text="Home",
style="TButton",
command=lambda: self.show_frame(HomePage),
)
self.home_button.pack(side=tk.LEFT, padx=10)
self.tv_button = ttk.Button(
self.nav_buttons_frame,
text="TV",
style="TButton",
command=lambda: self.show_frame(TVPage),
)
self.tv_button.pack(side=tk.LEFT, padx=10)
self.movies_button = ttk.Button(
self.nav_buttons_frame,
text="Movies",
style="TButton",
command=lambda: self.show_frame(MoviesPage),
)
self.movies_button.pack(side=tk.LEFT, padx=10)
def show_frame(self, cont):
"""Show a frame for the given class"""
if cont == HomePage:
frame = self.frames[HomePage]
frame.update_display(self.tv_shows, self.movies)
elif cont == MoviesPage:
frame = self.frames[MoviesPage]
frame.update_display(self.movies) # Update to show only movies
elif cont == TVPage:
frame = self.frames[TVPage]
frame.update_display(self.tv_shows) # Update to show only TV shows
frame.tkraise()
def toggle_search(self):
# Toggle visibility of the search entry
if (
self.search_entry.winfo_manager()
): # If the entry is already managed by a geometry manager
self.search_entry.pack_forget() # Hide the search entry
else:
self.search_entry.pack(
side=tk.RIGHT, padx=10, fill=tk.X
) # Show the search entry
self.search_entry.focus() # Focus on the entry widget
# Bind the update function to the search variable
self.search_var.trace(
"w", lambda name, index, mode, sv=self.search_var: self.update_search(sv)
)
def update_search(self, search_var):
# Get the current search term
search_term = search_var.get().lower()
# Filter the movies and tv shows
filtered_movies = [
movie for movie in self.movies if search_term in movie["title"].lower()
]
filtered_tv_shows = [
tv_show
for tv_show in self.tv_shows
if search_term in tv_show["title"].lower()
]
# Update the displays
self.frames[HomePage].update_display(filtered_tv_shows, filtered_movies)
self.frames[MoviesPage].update_display(filtered_movies)
self.frames[TVPage].update_display(filtered_tv_shows)
class HomePage(tk.Frame):
"""HomePage class that displays sections for TV shows and movies"""
def __init__(self, master=None, *args, **kwargs):
super().__init__(master, *args, **kwargs)
self.configure(bg="#2f4b7b")
self.movies = []
self.tv_shows = []
self.create_widgets()
def create_widgets(self):
self.tv_frame = tk.Frame(self, bg="#2f4b7b")
self.tv_frame.pack(
fill=tk.X, expand=True
) # Changed from fill=tk.BOTH to fill=tk.X
self.tv_heading = tk.Label(
self.tv_frame,
text="TV Shows",
bg="#2f4b7b",
fg="white",
font=("Arial", 16),
anchor="w",
)
self.tv_heading.pack(side=tk.TOP, pady=(10, 5), padx=(10, 0), fill=tk.X)
self.tv_canvas = tk.Canvas(self.tv_frame, bg="#2f4b7b", highlightthickness=0)
self.tv_canvas.pack(
side=tk.TOP, fill=tk.X, expand=True
) # Changed from fill=tk.BOTH to fill=tk.X
# Create a frame for movies and its heading
self.movies_frame = tk.Frame(self, bg="#2f4b7b")
self.movies_frame.pack(
fill=tk.X, expand=True
) # Changed from fill=tk.BOTH to fill=tk.X
self.movies_heading = tk.Label(
self.movies_frame,
text="Movies",
bg="#2f4b7b",
fg="white",
font=("Arial", 16),
anchor="w",
)
self.movies_heading.pack(side=tk.TOP, pady=(10, 5), padx=(10, 0), fill=tk.X)
self.movies_canvas = tk.Canvas(
self.movies_frame, bg="#2f4b7b", highlightthickness=0
)
self.movies_canvas.pack(side=tk.TOP, fill=tk.X, expand=True)
def update_display(self, tv_shows, movies):
# Update the TV shows display
self.display_content(self.tv_canvas, tv_shows)
# Update the movies display
self.display_content(self.movies_canvas, movies)
def display_content(self, canvas, content):
"""Generic function to display content on a given canvas."""
canvas.delete("all") # Clear the canvas
image_references = [] # Keep references to images to avoid garbage collection
# Display content in a grid layout
for index, item in enumerate(content):
row, col = divmod(index, 5) # 5 items per row
x, y = (col * 220) + 10, (row * 320) + 10 # Grid position with padding
# Load and resize image
image_path = f'assets/thumbnails/{item["title"].replace(" ", "_")}.jpg'
image = Image.open(image_path).resize((200, 300), Image.Resampling.LANCZOS)
photo = ImageTk.PhotoImage(image)
# Add image to canvas
canvas.create_image(x, y, anchor=tk.NW, image=photo)
canvas.create_text(
x + 100,
y + 310,
text=item["title"],
fill="white",
font=("Arial", 12),
anchor=tk.CENTER,
)
# Keep a reference to the photo
image_references.append(photo)
# Save image references to the canvas object to ensure they are not garbage collected
canvas.image_references = image_references
class MoviesPage(tk.Frame):
def __init__(self, master=None, *args, **kwargs):
super().__init__(master, *args, **kwargs)
self.configure(bg="#2f4b7b")
self.create_widgets()
def create_widgets(self):
# Movies heading
self.heading_label = tk.Label(
self,
text="Movies",
bg="#2f4b7b",
fg="white",
font=("Arial", 18),
anchor="w",
)
self.heading_label.pack(side=tk.TOP, fill=tk.X, padx=10, pady=10)
self.canvas = tk.Canvas(self, bg="#2f4b7b", highlightthickness=0)
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
def update_display(self, movies):
self.display_movies(movies)
def display_movies(self, movies):
"""Display a list of movies on the canvas."""
self.canvas.delete("all") # Clear the canvas
image_references = [] # Keep references to images to avoid garbage collection
# Display movies in a grid layout
for index, movie in enumerate(movies):
row, col = divmod(index, 5) # 5 movies per row
x, y = (col * 220) + 10, (row * 320) + 10 # Grid position with padding
# Load and resize image
image_path = f'assets/thumbnails/{movie["title"].replace(" ", "_")}.jpg'
image = Image.open(image_path).resize((200, 300), Image.Resampling.LANCZOS)
photo = ImageTk.PhotoImage(image)
# Add image to canvas
self.canvas.create_image(x, y, anchor=tk.NW, image=photo)
self.canvas.create_text(
x + 100,
y + 310,
text=movie["title"],
fill="white",
font=("Arial", 12),
anchor=tk.CENTER,
)
# Keep a reference to the photo
image_references.append(photo)
# Save image references to the canvas object to ensure they are not garbage collected
self.canvas.image_references = image_references
def create_search_bar(self):
# Search bar frame
self.search_bar_frame = tk.Frame(self, bg="#2f4b7b")
self.search_bar_frame.pack(side=tk.TOP, fill=tk.X)
# Search entry
self.search_var = tk.StringVar()
self.search_entry = tk.Entry(
self.search_bar_frame, textvariable=self.search_var, width=70
) # Adjust width as needed
self.search_entry.insert(
0, "Search for TV shows and movies"
) # Placeholder text
self.search_entry.bind(
"<FocusIn>",
lambda args: (
self.search_entry.delete("0", "end")
if self.search_entry.get() == "Search for TV shows and movies"
else None
),
)
self.search_entry.pack(side=tk.LEFT, padx=10, pady=5, fill=tk.X, expand=True)
# Search button - if you want to include a search button next to the entry
self.search_button = ttk.Button(
self.search_bar_frame, text="Search", command=self.perform_search
)
self.search_button.pack(side=tk.RIGHT, padx=10, pady=5)
class TVPage(tk.Frame):
def __init__(self, master=None, *args, **kwargs):
super().__init__(master, *args, **kwargs)
self.configure(bg="#2f4b7b")
self.create_widgets()
def create_widgets(self):
# TV Shows heading
self.heading_label = tk.Label(
self,
text="TV Shows",
bg="#2f4b7b",
fg="white",
font=("Arial", 18),
anchor="w",
)
self.heading_label.pack(side=tk.TOP, fill=tk.X, padx=10, pady=10)
self.canvas = tk.Canvas(self, bg="#2f4b7b", highlightthickness=0)
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
def update_display(self, movies):
self.display_movies(movies)
def display_movies(self, movies):
"""Display a list of movies on the canvas."""
self.canvas.delete("all") # Clear the canvas
image_references = [] # Keep references to images to avoid garbage collection
# Display movies in a grid layout
for index, movie in enumerate(movies):
row, col = divmod(index, 5) # 5 movies per row
x, y = (col * 220) + 10, (row * 320) + 10 # Grid position with padding
# Load and resize image
image_path = f'assets/thumbnails/{movie["title"].replace(" ", "_")}.jpg'
image = Image.open(image_path).resize((200, 300), Image.Resampling.LANCZOS)
photo = ImageTk.PhotoImage(image)
# Add image to canvas
self.canvas.create_image(x, y, anchor=tk.NW, image=photo)
self.canvas.create_text(
x + 100,
y + 310,
text=movie["title"],
fill="white",
font=("Arial", 12),
anchor=tk.CENTER,
)
# Keep a reference to the photo
image_references.append(photo)
# Save image references to the canvas object to ensure they are not garbage collected
self.canvas.image_references = image_references
def main():
app = MainApplication()
app.mainloop()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment