'tkinter listbox check new selection and activate from a button

I am trying to get the newly selected track from the playlist when the current song is either being played or has been paused. However, when I tried to do that, it would disrupt the pausing logic because then, it would only either start the music of the newly selected track or keep restarting the current track.

Below is a runnable program that only requires you to have just a folder with at least multiple tracks:

import os
import pygame
import tkinter as tk
from tkinter import Button, filedialog as fd


# pylint: disable="R0902, R0904"
class MusicPlayer(tk.Tk):
    def __init__(self) -> None:
        super().__init__()
        pygame.init()
        pygame.mixer.init()

        self.track_count: int = 0
        self.file_indices: int = 0

        self.song_list: list = []

        self.file_info: dict = {}

        self.is_music_paused: bool = False
        self.music_playing: bool = False

        self.menubar = None
        self.filemenu = None

        self.playlist_frame = None
        self.bottom_frame = None

        self.song_directory = None

        self.play_button = None

        self.configure_window()
        self.setup_bottom_frame()

    def configure_window(self) -> None:
        self.title("Music Player")
        self.configure(background="#212125")
        window_width: int = 1280
        window_height: int = 800
        screen_width: int = self.winfo_screenwidth()
        screen_height: int = self.winfo_screenheight()
        x_coordinate: int = int((screen_width / 2) - (window_width / 2))
        y_coordinate: int = int((screen_height / 2.5) - (window_height / 2.5))
        self.geometry(f"{window_width}x{window_height}+{x_coordinate}+{y_coordinate}")
        self.resizable(0, 0)
        self.setup_menubar()

    def setup_menubar(self) -> None:
        self.menubar = tk.Menu(self, tearoff=False)
        self.filemenu = tk.Menu(self.menubar, tearoff=False)
        self.filemenu.add_separator()
        self.filemenu.add_command(
            label="Open File...", font="Segoe 11", command=self.browse_file
        )
        self.filemenu.add_command(
            label="Open Folder...", font="Segoe 11", command=self.browse_directory
        )
        self.filemenu.add_separator()
        self.filemenu.add_command(label="Exit...", font="Segoe 11", command=exit)
        self.menubar.add_cascade(label="File", font="Segoe 11", menu=self.filemenu)
        self.config(menu=self.menubar)

    def setup_playlist_panel(self) -> None:
        self.playlist_frame = tk.Frame(self)
        self.playlist_frame.grid(row=0, column=0, sticky="w", padx=15)
        self.playlist_box = tk.Listbox(
            self.playlist_frame,
            bg="#111",
            fg="White",
            width=65,
            height=35,
            highlightthickness=0,
            relief=tk.FLAT,
        )
        self.playlist_box.grid(row=0, column=0)

        self.setup_scrollbar()

    def setup_scrollbar(self) -> None:
        self.vertical_scrollbar = tk.Scrollbar(self.playlist_frame, orient=tk.VERTICAL)
        self.vertical_scrollbar.grid(row=0, column=1, sticky="ns")
        self.horizontal_scrollbar = tk.Scrollbar(
            self.playlist_frame, orient=tk.HORIZONTAL
        )
        self.horizontal_scrollbar.grid(row=1, column=0, sticky="we")
        self.playlist_box.config(yscrollcommand=self.vertical_scrollbar.set)
        self.playlist_box.config(xscrollcommand=self.horizontal_scrollbar.set)
        self.vertical_scrollbar.config(command=self.playlist_box.yview)
        self.horizontal_scrollbar.config(command=self.playlist_box.xview)

    def setup_bottom_frame(self) -> None:
        self.bottom_frame = tk.Frame(bg="silver")
        self.bottom_frame.grid(row=1, column=0, sticky="ew")
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)

        self.setup_buttons()

    def setup_buttons(self) -> None:
        self.play_button = Button(
            self.bottom_frame,
            relief=tk.FLAT,
            text="Play",
            font="Helvetica 14 bold",
            bg="silver",
            highlightthickness=0,
            command=self.toggle_play_pause,
        )
        self.play_button.grid(row=0, column=0, padx=67, pady=15)

    def browse_directory(self) -> None:
        self.song_directory: str = fd.askdirectory(
            initialdir=os.getcwd(), title="Select Directory With Songs"
        )

        if self.song_directory:
            self.setup_playlist_panel()
            self.play_button.config(state=tk.NORMAL)

        self.load_directory_songs()

    def load_directory_songs(self) -> None:
        file_extension: tuple[str] = (
            "mp3",
            "wav",
            "ogg",
        )

        for file_index, song_filename in enumerate(os.listdir(self.song_directory)):
            song_path = os.path.join(self.song_directory, song_filename)
            if song_filename.endswith(file_extension):
                self.track_count += 1
                self.song_list.append(song_filename)
                self.playlist_box.insert(tk.END, f"{self.track_count}. {song_filename}")
                self.file_info[file_index] = [song_filename, song_path]

            self.file_indices += 1

    def start_music_playlist(self) -> None:
        self.track_info = self.file_info[self.playlist_box.curselection()[0]]
        self.play_button.config(text="Pause", font="Helvetica 14 bold")
        pygame.mixer.music.load(self.track_info[1])
        pygame.mixer.music.play()

    def pause_music(self) -> None:
        pygame.mixer.music.pause()
        self.play_button.config(text="Resume", font="Helvetica 14 bold")
        self.is_music_paused = True

    def unpause_music(self) -> None:
        pygame.mixer.music.unpause()
        self.play_button.config(text="Pause", font="Helvetica 14 bold")
        self.is_music_paused = False

    def play_music(self) -> None:
        if self.song_directory is not None:
            self.start_music_playlist()

        self.music_playing = True

    def toggle_play_pause(self) -> None:
        if not self.music_playing:
            self.play_music()
        else:
            if self.is_music_paused:
                self.unpause_music()
            elif not self.is_music_paused:
                self.pause_music()


if __name__ == "__main__":
    MusicPlayer().mainloop()

To make things easier, the issue starts from def start_music_playlist(self): onward. I have tried to get the info of the newly selected track by doing some conditional testing in the pause/unpause methods like:

new_selection = self.file_info[self.playlist_box.curselection()[0]]

if new_selection:
    pygame.mixer.music.load(self.track_info[1])
    pygame.mixer.music.play()
else:
    pygame.mixer.music.unpause()
    self.play_button.config(text="Pause", font="Helvetica 14 bold")
    self.is_music_paused = False

I have also found that self.music_playing = True affects the pausing logic. Without it, it won't pause the song as the track will keep restarting, but then without it, I can play any newly selected track.

I'd really appreciate it if anyone can help me out on this. Thank You.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source