I want to be able to resize my widget/windows with with the double arrows Window’s style grab edge and resize them type functionality. I want to able to change height and width by dragging the edges of each window individually to adjust them. Any help on how to refine this script and suggestions on how to make it better would be greatly appreciated.
Here is my script
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from yt_dlp import YoutubeDL
import vlc
import os
import random
import json
import threading
from PIL import Image, ImageTk
class YouTubeJukebox(tk.Tk):
def __init__(self):
super().__init__()
self.title("YouTube Jukebox by Littch")
# Detect screen width and height
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
# Set geometry to maximize the window
self.geometry(f"{screen_width}x{screen_height}")
# Load background image
self.bg_image = Image.open(r"C:UsersJackDesktopdj-8562307.jpg")
self.bg_image = self.bg_image.resize((screen_width, screen_height), Image.LANCZOS)
self.bg_photo = ImageTk.PhotoImage(self.bg_image)
# Create a canvas and set the background image
self.canvas = tk.Canvas(self, width=screen_width, height=screen_height)
self.canvas.pack(fill="both", expand=True)
self.canvas.create_image(0, 0, image=self.bg_photo, anchor="nw")
# VLC Instance and Player Setup
self.vlc_instance = vlc.Instance()
self.player = self.vlc_instance.media_player_new()
self.player.event_manager().event_attach(vlc.EventType.MediaPlayerEndReached, self.on_song_end)
# Styling
style = ttk.Style(self)
style.configure('TButton', font=('Arial', 7), padding=5)
style.map('TButton', foreground=[('pressed', 'white'), ('active', 'white')],
background=[('pressed', '#3498DB'), ('active', '#3498DB')])
self.playlist_id_entry_style = {"bg": "#ECF0F1", "fg": "#34495E", "insertbackground": "#34495E"}
self.playlist_id_label_style = {"bg": "#2C3E50", "fg": "#FFFFFF"}
# Add widgets to the canvas
self.label = tk.Label(self, text="Enter YouTube Playlist ID:", **self.playlist_id_label_style)
self.label_window = self.canvas.create_window(screen_width//2, 50, anchor="center", window=self.label)
self.playlist_id_entry = tk.Entry(self, width=50, **self.playlist_id_entry_style)
self.entry_window = self.canvas.create_window(screen_width//2, 100, anchor="center", window=self.playlist_id_entry)
self.load_button = ttk.Button(self, text="Load Playlist", command=self.load_playlist)
self.load_button_window = self.canvas.create_window(screen_width//2, 150, anchor="center", window=self.load_button)
self.listbox = tk.Listbox(self, width=80, height=15, bg="#424242", fg="#FFFFFF", selectbackground="#FF5722")
self.listbox_window = self.canvas.create_window(screen_width//2, 300, anchor="center", window=self.listbox)
self.controls_frame = tk.Frame(self, bg="#212121")
self.controls_frame_window = self.canvas.create_window(screen_width//2, 450, anchor="center", window=self.controls_frame)
button_style = {'width': 6, 'height': 1}
self.play_button = ttk.Button(self.controls_frame, text="Play", command=self.play_selected)
self.play_button.pack(side=tk.LEFT, padx=5, pady=5)
self.pause_button = ttk.Button(self.controls_frame, text="Pause", command=self.pause_audio)
self.pause_button.pack(side=tk.LEFT, padx=5, pady=5)
self.play_all_button = ttk.Button(self.controls_frame, text="Play All", command=self.play_all)
self.play_all_button.pack(side=tk.LEFT, padx=5, pady=5)
self.skip_back_button = ttk.Button(self.controls_frame, text="Skip Back", command=self.skip_back)
self.skip_back_button.pack(side=tk.LEFT, padx=5, pady=5)
self.skip_forward_button = ttk.Button(self.controls_frame, text="Skip Forward", command=self.skip_forward)
self.skip_forward_button.pack(side=tk.LEFT, padx=5, pady=5)
self.shuffle_button = ttk.Button(self.controls_frame, text="Shuffle", command=self.shuffle_playlist)
self.shuffle_button.pack(side=tk.LEFT, padx=5, pady=5)
self.save_button = ttk.Button(self.controls_frame, text="Save Playlist", command=self.save_playlist)
self.save_button.pack(side=tk.LEFT, padx=5, pady=5)
self.load_playlist_button = ttk.Button(self.controls_frame, text="Load Playlist", command=self.load_saved_playlist)
self.load_playlist_button.pack(side=tk.LEFT, padx=5, pady=5)
self.volume_label = tk.Label(self, text="Volume:", fg="#FFFFFF", bg="#212121")
self.volume_label_window = self.canvas.create_window(200, 500, anchor="center", window=self.volume_label)
self.volume_slider = tk.Scale(self, from_=0, to=100, orient=tk.HORIZONTAL, command=self.set_volume, length=300)
self.volume_slider.set(50)
self.volume_slider_window = self.canvas.create_window(screen_width//2, 550, anchor="center", window=self.volume_slider)
self.equalizer_frame = tk.Frame(self, bg="#212121")
self.equalizer_frame_window = self.canvas.create_window(screen_width//2, 600, anchor="center", window=self.equalizer_frame)
self.equalizer = vlc.AudioEqualizer()
self.create_equalizer_controls()
self.playlist = None
self.current_index = None
self.audio_urls = []
self.media_list = None
self.media_player = None
self.last_playlists_file = "last_playlists.json"
self.last_playlists = []
# Bindings for moving and resizing
self.bind_events()
def bind_events(self):
for widget in [self.label, self.playlist_id_entry, self.load_button, self.listbox, self.controls_frame, self.volume_label, self.volume_slider, self.equalizer_frame]:
widget.bind("<Button-1>", self.on_widget_click)
widget.bind("<B1-Motion>", self.on_drag)
widget.bind("<ButtonRelease-1>", self.on_release)
widget.bind("<Enter>", self.on_enter)
widget.bind("<Leave>", self.on_leave)
self.drag_data = {"x": 0, "y": 0, "widget": None}
self.resize_data = {"resizing": False, "widget": None, "direction": None}
def on_widget_click(self, event):
widget = event.widget
self.drag_data["widget"] = widget
self.drag_data["x"] = event.x
self.drag_data["y"] = event.y
if self.resize_data["resizing"]:
self.resize_data["widget"] = widget
def on_drag(self, event):
widget = self.drag_data["widget"]
if widget and not self.resize_data["resizing"]:
x = widget.winfo_x() + event.x - self.drag_data["x"]
y = widget.winfo_y() + event.y - self.drag_data["y"]
widget.place(x=x, y=y)
elif self.resize_data["resizing"]:
self.resize_widget(event)
def on_release(self, event):
self.drag_data["widget"] = None
self.resize_data["resizing"] = False
def on_enter(self, event):
widget = event.widget
widget.update_idletasks()
bbox = widget.winfo_geometry().split('+')[0].split('x')
width = int(bbox[0])
height = int(bbox[1])
if abs(event.x - width) < 5 or abs(event.y - height) < 5:
self.canvas.config(cursor="sizing")
self.resize_data["resizing"] = True
else:
self.canvas.config(cursor="")
def on_leave(self, event):
self.canvas.config(cursor="")
def resize_widget(self, event):
widget = self.resize_data["widget"]
widget.update_idletasks()
width = widget.winfo_width()
height = widget.winfo_height()
if abs(event.x - width) < 5:
widget.config(width=event.x)
elif abs(event.y - height) < 5:
widget.config(height=event.y)
def load_playlist(self):
def load_playlist_async():
try:
playlist_id = self.playlist_id_entry.get().split('&')[0]
if not playlist_id:
messagebox.showerror("Error", "Please enter a valid playlist ID")
return
playlist_url = f"https://www.youtube.com/playlist?list={playlist_id}"
self.playlist = self.get_playlist_videos(playlist_url)
self.listbox.delete(0, tk.END)
for title in for video in self.playlist]:
self.listbox.insert(tk.END, title)
self.current_index = 0
except Exception as e:
print(f"Error loading playlist: {e}")
messagebox.showerror("Error", f"Failed to load playlist: {e}")
threading.Thread(target=load_playlist_async).start()
def get_playlist_videos(self, playlist_url):
ydl_opts = {
'extract_flat': True,
'quiet': True,
'force_generic_extractor': True
}
with YoutubeDL(ydl_opts) as ydl:
result = ydl.extract_info(playlist_url, download=False)
if 'entries' in result:
return result['entries']
else:
return []
def play_all(self):
def play_all_async():
try:
if not self.playlist:
messagebox.showerror("Error", "Please load a playlist first")
return
self.current_index = 0
self.play_current_index()
except Exception as e:
print(f"Error in play_all: {e}")
threading.Thread(target=play_all_async).start()
def play_selected(self):
def play_selected_async():
try:
if not self.playlist:
messagebox.showerror("Error", "Please load a playlist")
return
selected_index = self.listbox.curselection()
if selected_index:
self.current_index = selected_index[0]
self.play_current_index()
except Exception as e:
print(f"Error in play_selected: {e}")
threading.Thread(target=play_selected_async).start()
def play_current_index(self):
def play_current_index_async():
try:
if self.current_index is None or self.current_index >= len(self.playlist):
messagebox.showerror("Error", "Please select a valid video")
return
video_url = f"https://www.youtube.com/watch?v={self.playlist[self.current_index]['id']}"
audio_url = self.get_best_audio_url(video_url)
media = self.vlc_instance.media_new(audio_url)
self.player.set_media(media)
self.set_volume(self.volume_slider.get())
self.player.play()
self.highlight_current_song()
except Exception as e:
print(f"Error in play_current_index: {e}")
threading.Thread(target=play_current_index_async).start()
def get_best_audio_url(self, video_url):
ydl_opts = {
'format': 'bestaudio',
'quiet': True
}
with YoutubeDL(ydl_opts) as ydl:
info_dict = ydl.extract_info(video_url, download=False)
return info_dict['url']
def highlight_current_song(self):
try:
self.listbox.select_clear(0, tk.END)
self.listbox.select_set(self.current_index)
self.listbox.activate(self.current_index)
except Exception as e:
print(f"Error in highlight_current_song: {e}")
def pause_audio(self):
try:
if self.player.is_playing():
self.player.pause()
except Exception as e:
print(f"Error in pause_audio: {e}")
def on_song_end(self, event):
self.current_index += 1
if self.current_index < len(self.playlist):
self.play_current_index()
def skip_forward(self):
try:
if self.current_index is not None:
self.current_index = (self.current_index + 1) % len(self.playlist)
self.play_current_index()
except Exception as e:
print(f"Error in skip_forward: {e}")
def skip_back(self):
try:
if self.current_index is not None:
self.current_index = (self.current_index - 1) % len(self.playlist)
self.play_current_index()
except Exception as e:
print(f"Error in skip_back: {e}")
def shuffle_playlist(self):
try:
if self.playlist:
random.shuffle(self.playlist)
self.listbox.delete(0, tk.END)
for video in self.playlist:
self.listbox.insert(tk.END, video['title'])
self.current_index = 0
except Exception as e:
print(f"Error in shuffle_playlist: {e}")
def set_volume(self, volume):
try:
self.player.audio_set_volume(int(volume))
except Exception as e:
print(f"Error in set_volume: {e}")
def save_playlist(self):
try:
playlist_id = self.playlist_id_entry.get().split('&')[0]
self.load_last_playlists()
if playlist_id and playlist_id not in self.last_playlists:
self.last_playlists.append(playlist_id)
with open(self.last_playlists_file, "w") as file:
json.dump(self.last_playlists, file)
messagebox.showinfo("Success", "Playlist saved successfully")
else:
messagebox.showinfo("Info", "Playlist is already saved")
except Exception as e:
print(f"Error in save_playlist: {e}")
def load_last_playlists(self):
try:
if os.path.exists(self.last_playlists_file):
with open(self.last_playlists_file, "r") as file:
self.last_playlists = json.load(file)
else:
self.last_playlists = []
except Exception as e:
print(f"Error in load_last_playlists: {e}")
def load_saved_playlist(self):
try:
self.load_last_playlists()
if not self.last_playlists:
messagebox.showinfo("Info", "No saved playlists found")
return
playlist_id = random.choice(self.last_playlists)
self.playlist_id_entry.delete(0, tk.END)
self.playlist_id_entry.insert(0, playlist_id)
self.load_playlist()
except Exception as e:
print(f"Error in load_saved_playlist: {e}")
def create_equalizer_controls(self):
labels = ["60Hz", "170Hz", "310Hz", "600Hz", "1kHz", "3kHz", "6kHz", "12kHz", "14kHz", "16kHz"]
for i, label in enumerate(labels):
lbl = tk.Label(self.equalizer_frame, text=label, fg="#FFFFFF", bg="#212121")
lbl.grid(row=0, column=i, padx=2, pady=2)
scale = tk.Scale(self.equalizer_frame, from_=-20, to=20, orient=tk.HORIZONTAL, length=70, command=lambda val, idx=i: self.set_equalizer_band(idx, val))
scale.grid(row=1, column=i, padx=2, pady=2)
def set_equalizer_band(self, band, value):
try:
self.equalizer.set_amp_at_index(float(value), band)
self.player.set_equalizer(self.equalizer)
except Exception as e:
print(f"Error setting equalizer band: {e}")
if __name__ == "__main__":
app = YouTubeJukebox()
app.mainloop()
jacknbiloxi W is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.