Struggling with UI issues

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()

New contributor

jacknbiloxi W is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật