Python Forum
Basic Music Player with tkinter
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Basic Music Player with tkinter
#2
Version 2 has a few tweaks. Still to do - make a play all option and clean up the code.
#! /usr/bin/env python3
import pygame as pg
import tkinter as tk
from tkinter import filedialog, messagebox
from os import chdir, listdir, sys
from functools import partial


PAUSE = pg.USEREVENT+1
TRACK_END = pg.USEREVENT+1

class MusicFolder:
    def __init__(self):
        self.folder = None

    def get_files(self, tracklist, *args, **kwargs):
        self.folder = kwargs['folder']
        tracklist.delete(0, tk.END)
        chdir(self.folder)
        tracks = []
        formats = ['mp3', 'wav', 'ogg']
        playlist = listdir()
        for track in playlist:
            if track[-3:] in formats:
                tracklist.insert(tk.END, track)
        tracklist.select_set(0)

class Controls:
    def __init__(self):
        pass

    def play(self, *args, **kwargs):
        file = f'{kwargs["folder"]}/{kwargs["active"]}'
        pg.mixer.music.set_endevent(TRACK_END)
        try :
            pg.mixer.music.load(file)
            pg.mixer.music.play()
        except Exception:
            messagebox.showerror(title='No folder selected', \
            message='You must select a music folder to load the play list. The program will exit now.')
            sys.exit()

    def pause(self):
        pg.mixer.music.pause()

    def unpause(self):
        pg.mixer.music.unpause()

    def next(self, *args, **kwargs):
        pass

    def prev(self, *args, **kwargs):
        pass

    def stop(self, *args, **kwargs):
        pg.mixer.music.stop()
        pg.mixer.music.set_endevent()
        file = f'{kwargs["folder"]}/{kwargs["active"]}'
        pg.mixer.music.set_endevent(TRACK_END)
        pg.mixer.music.load(file)


class Player:
    def __init__(self, parent):
        self.parent = parent
        self.parent.update()
        self.width = self.parent.winfo_width()
        self.height = self.parent.winfo_height()
        self.parent.grid_columnconfigure(0, weight=1)
        self.parent.grid_rowconfigure(0, weight=1)
        bgcolor = 'light blue'
        fgcolor = 'navy'
        self.folder = None
        pg.init()
        pg.mixer.init()


        pg.mixer.music.set_endevent(TRACK_END)

        self.control = Controls()
        self.music = MusicFolder()

        # Container
        container = tk.Frame(self.parent)
        container.grid(column=0, row=0, sticky='news')

        # Contains the header image/canvas
        headerframe = tk.Frame(container)
        headerframe.grid(column=0, row=0, sticky='new', pady=2, padx=2)

        # Contains 3 columns of info - status/track/choose folder button
        frame1 = tk.Frame(container)
        frame1.grid(column=0, row=1, sticky='new', pady=2)

        # Status Label
        self.status_label = tk.Label(frame1, padx=8, bg=bgcolor, fg=fgcolor, \
        width=17, text='No status', anchor='w')
        self.status_label['bd'] = 1
        self.status_label['relief'] = 'ridge'
        self.status_label.grid(column=0, row=0, sticky='news', padx=2)

        # Track playing label
        self.track_label = tk.Label(frame1, padx=8, bg=bgcolor, fg=fgcolor, \
        width=70, text='No track is playing', anchor='w')
        self.track_label['bd'] = 1
        self.track_label['relief'] = 'ridge'
        self.track_label.grid(column=1, row=0, sticky='news', padx=4)

        # Button for populating our listbox with tracks
        self.button = tk.Button(frame1, text='Choose Music Folder', \
        fg='navy', bg='lightsteelblue')
        self.button['command'] = partial(self.get_music)
        self.button.grid(column=2, row=0, sticky='new', padx=2)
        self.button.bind('<Enter>', partial(self.on_enter, self.button))
        self.button.bind('<Leave>', partial(self.on_exit, self.button))

        # Contains 3 columns - spacer/listbox/scrollbar
        frame2 = tk.Frame(container)
        frame2.grid(column=0, row=2, sticky='new', pady=2)
        frame2.grid_columnconfigure(0, weight=3)

        # Just a spacer label. May use to show album image?
        spacer_label = tk.Label(frame2, bg='silver', bd=1, relief='ridge')
        spacer_label['height'] = 15
        spacer_label['width'] = 30
        spacer_label.grid(column=0, row=0, sticky='news', padx=2)

        # Frame for listbox to give appearence of text not against side
        padframe = tk.Frame(frame2, bd=1, relief='ridge', bg='aliceblue', padx=8, \
        pady=5)
        padframe['highlightcolor'] = '#999999'
        padframe['highlightbackground'] = '#999999'
        padframe['highlightthickness'] = 1
        padframe.grid(column=2, row=0, sticky='news', padx=2)
        padframe.grid_rowconfigure(0, weight=3)
        padframe.grid_columnconfigure(0, weight=3)

        # Listbox and scrollbar
        self.scrollbar = tk.Scrollbar(frame2, orient='vertical')
        self.playlist = tk.Listbox(padframe, width=70, bd=0, bg='aliceblue')
        self.playlist['yscrollcommand'] = self.scrollbar.set
        self.playlist['selectmode'] = 'single'
        self.playlist['selectbackground'] = 'lightsteelblue'
        self.playlist['selectforeground'] = 'navy'
        self.playlist['highlightcolor'] = 'white'
        self.playlist['highlightbackground'] = 'white'
        self.playlist['highlightthickness'] = 0
        self.playlist['bd'] = 0
        self.playlist.grid(column=0, row=0, sticky='news')
        self.scrollbar.grid(column=3, row=0, sticky='ns', padx=2)

        # Contains the control buttons - play/stop/next/prev
        frame3 = tk.Frame(container)
        frame3.grid(column=0, row=3, sticky='new', pady=2)
        for i in range(4):
            frame3.grid_columnconfigure(i, weight=3, uniform='control_btns')

        # The buttons - play/stop/next/prev
        # play button will double as a pause button
        self.play_btn = tk.Button(frame3, text='Play', fg='navy', bg='lightsteelblue')
        self.play_btn.grid(column=0, row=0, sticky='new', padx=2)
        self.play_btn['command'] = partial(self.play, state='play')
        self.play_btn.bind('<Enter>', partial(self.on_enter, self.play_btn))
        self.play_btn.bind('<Leave>', partial(self.on_exit, self.play_btn))


        self.stop_btn = tk.Button(frame3, text='Stop', fg='navy', bg='lightsteelblue')
        self.stop_btn.grid(column=1, row=0, sticky='new', padx=2)
        self.stop_btn['command'] = partial(self.play, state='stop')
        self.stop_btn.bind('<Enter>', partial(self.on_enter, self.stop_btn))
        self.stop_btn.bind('<Leave>', partial(self.on_exit, self.stop_btn))


        self.next_btn = tk.Button(frame3, text='Next', fg='navy', bg='lightsteelblue')
        self.next_btn.grid(column=2, row=0, sticky='new', padx=2)
        self.next_btn['command'] = partial(self.next)
        self.next_btn.bind('<Enter>', partial(self.on_enter, self.next_btn))
        self.next_btn.bind('<Leave>', partial(self.on_exit, self.next_btn))

        self.back_btn = tk.Button(frame3, text='Prev', fg='navy', bg='lightsteelblue')
        self.back_btn.grid(column=3, row=0, sticky='new', padx=2)
        self.back_btn['command'] = partial(self.prev)
        self.back_btn.bind('<Enter>', partial(self.on_enter, self.back_btn))
        self.back_btn.bind('<Leave>', partial(self.on_exit, self.back_btn))


    #     self.update()
    #
    # def update(self):
    #     for event in pg.event.get():
    #         if event.type == TRACK_END:
    #             self.play_btn['text'] = 'Play'
    #             self.status_label['text'] = 'No status'
    #             self.track_label['text'] = 'No track is playing'
    #         pass
    #
    #     self.parent.after(100, self.update)

    def get_music(self):
        self.folder = filedialog.askdirectory()
        self.music.get_files(self.playlist, folder=self.folder)

    # Define some button animations
    def on_enter(self, btn, event):
        btn['bg'] = 'powderblue'
        btn['fg'] = 'navy'
        btn['cursor'] = 'hand2'

    def on_exit(self, btn, event):
        btn['bg'] = 'lightsteelblue'
        btn['fg'] = 'navy'

    def next(self):
        pg.mixer.music.stop()
        index = self.playlist.curselection()
        if index:
            next_index = 0
            if len(index) > 0:
                last_index = int(index[-1])
                self.playlist.selection_clear(index)

                if last_index < self.playlist.size()-1:
                    next_index = last_index + 1
            self.playlist.activate(next_index)
            self.playlist.selection_set(next_index)
            self.status_label['text'] = 'Now Playing here'
            self.play(state='play')
        else:
            pass

    def prev(self):
        try:
            pg.mixer.music.stop()
            index = self.playlist.curselection()
            last_index = int(index[-1])
            if last_index == 0:
                last_index = self.playlist.size()
            self.playlist.selection_clear(index)
            last_index = last_index - 1
            self.playlist.activate(last_index)
            self.playlist.selection_set(last_index)
            self.play(state='play')
        except Exception:
            pass

    def play(self, *args, **kwargs):
        if self.playlist.get(tk.ACTIVE):
            state = kwargs['state']
            self.track_label['text'] = self.playlist.get(tk.ACTIVE)[:-4]

            if state == 'play':
                self.play_btn['text'] = 'Pause'
                self.play_btn['command'] = partial(self.play, state='pause')
                self.status_label['text'] = 'Now Playing'
                self.control.play(active=self.playlist.get(tk.ACTIVE), folder=self.folder)


            elif state == 'pause':
                self.play_btn['text'] = 'Resume'
                self.play_btn['command'] = partial(self.play, state='unpause')
                self.status_label['text'] = 'Paused'
                self.control.pause()

            elif state == 'unpause':
                self.play_btn['text'] = 'Pause'
                self.play_btn['command'] = partial(self.play, state='pause')
                self.status_label['text'] = 'Now Playing'
                self.control.unpause()

            else:
                try:
                    index = self.playlist.curselection()
                    self.play_btn['text'] = 'Play'
                    self.play_btn['command'] = partial(self.play, state='play')
                    self.status_label['text'] = 'No status'
                    self.track_label['text'] = 'No track is playing'
                    self.playlist.selection_clear(index)
                    self.playlist.select_set(0)
                    self.playlist.activate(0)
                    self.control.stop(folder=self.folder, active=self.playlist.get(tk.ACTIVE))
                except Exception:
                    pass
        else:
            messagebox.showerror(title='No folder selected.', message='Please choose a folder with music files.')
            pass




def main():
    root = tk.Tk()
    root.title('Tkinter Music Player')
    root.geometry('805x315+250+250')
    root.resizable(0, 0)
    root['padx'] = 10
    root['pady'] = 5
    Player(root)
    root.mainloop()

if __name__ == '__main__':
    main()

# pg.mixer.music.load(f'{folder}/{playlist[0]}')
# pg.mixer.music.play()
# playlist.pop(0)
#
# running = True
# while running:
#     TRACK_END = pg.USEREVENT+1
#     pg.mixer.music.set_endevent(TRACK_END)
#
#     for event in pg.event.get():
#         if event.type == TRACK_END:
#             if len(playlist) > 0:
#                 pg.mixer.music.load(f'{folder}/{playlist[0]}')
#                 playlist.pop(0)
#                 pg.mixer.music.play()
#             else:
#                 running = False
#                 break
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply


Messages In This Thread
Basic Music Player with tkinter - by menator01 - Jul-15-2021, 03:58 PM
RE: Basic Music Player with tkinter - by menator01 - Jul-26-2021, 07:23 PM
RE: Basic Music Player with tkinter - by ndc85430 - Jul-30-2021, 10:48 AM
RE: Basic Music Player with tkinter - by menator01 - Jul-30-2021, 06:44 PM
RE: Basic Music Player with tkinter - by ndc85430 - Jul-31-2021, 04:27 AM

Possibly Related Threads…
Thread Author Replies Views Last Post
  PyQt5 Music Player menator01 22 14,845 Nov-03-2021, 05:59 PM
Last Post: Axel_Erfurt
  Tkinter basic email client menator01 4 3,713 Jul-23-2020, 08:16 AM
Last Post: ndc85430

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020