Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!
Resource icon

PyQt5 - Python 3.12 Music Player - Part 5

In the final part of the tutorial we will add some dynamic text and images to our app
In our imports add
Python:
from PyQt5.QtGui import QPixmap

Add the class
Python:
class Data:
    def __init__(self):
        pass

    def data(self, player):
        info = {}
        info['artist'] = player.metaData(QMediaMetaData.AlbumArtist)
        info['album'] = player.metaData(QMediaMetaData.AlbumTitle)
        info['track'] = player.metaData(QMediaMetaData.Title)
        info['release'] = player.metaData(QMediaMetaData.Year)
        info['genre'] = player.metaData(QMediaMetaData.Genre)
        info['coverart'] = player.metaData(QMediaMetaData.CoverArtImage)
        return info

In the window class in this section

Python:
# Empty labels used as place holders. Text will show when a track is playing
        self.artist = QLabel()
        self.album = QLabel()
        self._track = QLabel()
        self.release = QLabel()
        self.genre = QLabel()
        self.art = QLabel()

add
Python:
imgbox = QVBoxLayout()

img_frame = QFrame()
img_frame.setFixedSize(400,350)
img_frame.setLayout(imgbox)

imgbox.addWidget(self.art)

In the info_container.add change to
Python:
info_container.addWidget(img_frame, 5, 0, 1, 2)
There should be only 5 rows. Think in the original there were 6

In the controller class added a new function
Python:
def meta(self):
    if self.player.isMetaDataAvailable():
        data = self.data.data(self.player)
        self.window.artist.setText(data['artist'])
        self.window.album.setText(self.truncate(data['album'].strip('(')))
        self.window._track.setText(self.truncate(data['track'].strip('(')))
        self.window.release.setText(str(data['release']))
        self.window.genre.setText(data['genre'])

        pixmap = QPixmap(data['coverart'])
        pixmap = pixmap.scaled(380,320)
        self.window.art.setPixmap(pixmap)

        self.window.track.setText(f'Track: {self.truncate(data['track'])}')

Updated these two functions
Python:
# Update the state function to this
def state(self):
        ''' Method checks the state of the music player and append correct text
            to the play button '''
        if self.playlist.mediaCount() > 0:
            if self.player.state() != QMediaPlayer.PlayingState:
                self.window.play.setText('Paused')
                self.window.status.setText('Status: Now Playing')
                self.player.play()
            else:
                self.window.play.setText('Play')
                self.window.status.setText('Status: Now Paused')
                self.player.pause()
              
              
# Updated the clear function to this
def clear(self):
        ''' Method clears everthing. More will be added here later. '''
        self.player.stop()
        self.window.music_list.clear()
        self.playlist.clear()
        self.window.play.setText('Play')
        self.window.artist.setText('')
        self.window.album.setText('')
        self.window._track.setText('')
        self.window.release.setText('')
        self.window.genre.setText('')
        self.window.art.setPixmap(QPixmap())

In case I made any errors, here is the full code
Python:
# Do the imports
import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QGridLayout, QLabel,
                             QFrame, QHBoxLayout, QPushButton, QListWidget, QVBoxLayout, \
                                QDial, QFileDialog)
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtMultimedia import (QMediaPlayer, QMediaContent, QMediaPlaylist,
                                QMediaMetaData)
from PyQt5.QtGui import QPixmap
from mutagen.mp3 import MP3

class Data:
    def __init__(self):
        pass

    def data(self, player):
        info = {}
        info['artist'] = player.metaData(QMediaMetaData.AlbumArtist)
        info['album'] = player.metaData(QMediaMetaData.AlbumTitle)
        info['track'] = player.metaData(QMediaMetaData.Title)
        info['release'] = player.metaData(QMediaMetaData.Year)
        info['genre'] = player.metaData(QMediaMetaData.Genre)
        info['coverart'] = player.metaData(QMediaMetaData.CoverArtImage)
        return info


class Window(QMainWindow):
    ''' Class will handle the display '''
    def __init__(self):
        QMainWindow.__init__(self)

        self.setWindowTitle('My Music Player')

        # Sets a fixed size for the window
        self.setFixedSize(1280,720)

        # Create a main container - will hold all other widgets and layouts
        container = QGridLayout()

        # Create a header label
        header = QLabel('My Music Player')
        header.setFrameShape(QFrame.Shape.Box)
        header.setFrameShadow(QFrame.Shadow.Raised)
        header.setLineWidth(3)

        # Just like in css - set color and background color and the like. Is a bit limited.
        header.setStyleSheet('font-size: 48px; color: gold; \
                             font-weight: bold; font-family: comic sans ms; \
                             background-color: seagreen; padding: 10px;')
      
        # Aligning the text to the center
        header.setAlignment(Qt.AlignmentFlag.AlignCenter)

        # Set some common font styles
        common_style = ''' font-weight: bold; font-size: 10pt; padding: 3px; '''

        # Status Bars
        self.status = QLabel('Status: Now Stopped')
        self.status.setStyleSheet(common_style)
        self.status.setFrameShape(QFrame.Shape.Box)
        self.status.setFrameShadow(QFrame.Shadow.Sunken)

        self.track = QLabel('Track: ')
        self.track.setStyleSheet(common_style)
        self.track.setFrameShape(QFrame.Shape.Box)
        self.track.setFrameShadow(QFrame.Shadow.Sunken)

        # To Buttons
        btn_style = ''' QPushButton{background-color: skyblue;}
                        QPushButton:hover{background-color: lightskyblue; \
                            color: dodgetblue; font-weight: bold;}'''
      
        self.file_button = QPushButton('Get Audio')
        self.file_button.setStyleSheet(btn_style)
        self.file_button.setCursor(Qt.CursorShape.PointingHandCursor)

        self.clear_button = QPushButton('Clear List')
        self.clear_button.setStyleSheet(btn_style)
        self.clear_button.setCursor(Qt.CursorShape.PointingHandCursor)

        # Continaer to hold top button
        box1 = QHBoxLayout()

        box1.addWidget(self.file_button)
        box1.addWidget(self.clear_button)

        # Info Containers
        info_container = QGridLayout()

        # Using a frame to get a border around the info_container
        frame = QFrame()
        frame.setFrameShape(QFrame.Shape.Box)
        frame.setFrameShadow(QFrame.Shadow.Sunken)
        frame.setLayout(info_container)

      
        # Artist Labels Static
        artist = QLabel('Artist:')
        album = QLabel('Album:')
        _track = QLabel('Track:')
        release = QLabel('Album Release:')
        genre = QLabel('Genre:')

        # Empty labels used as place holders. Text will show when a track is playing
        self.artist = QLabel()
        self.album = QLabel()
        self._track = QLabel()
        self.release = QLabel()
        self.genre = QLabel()
        self.art = QLabel()

        imgbox = QVBoxLayout()

        img_frame = QFrame()
        img_frame.setFixedSize(400,350)
        img_frame.setLayout(imgbox)

        imgbox.addWidget(self.art)
      

      

        # Numers relate to a grid - row, column, occupy how many rows, how many columns
        info_container.addWidget(artist, 0, 0, 1, 1)
        info_container.addWidget(self.artist, 0, 1, 1, 1)

        info_container.addWidget(album, 1, 0, 1, 1)
        info_container.addWidget(self.album, 1, 1, 1, 1)

        info_container.addWidget(_track, 2, 0, 1, 1)
        info_container.addWidget(self._track, 2, 1, 1, 1)

        info_container.addWidget(release, 3, 0, 1, 1)
        info_container.addWidget(self.release, 3, 1, 1, 1)

        info_container.addWidget(genre, 4, 0, 1, 1)
        info_container.addWidget(self.genre, 4, 1, 1, 1)

        info_container.addWidget(img_frame, 5, 0, 1, 2)

        # Create a listbox to hold tracks
        self.music_list = QListWidget()

        # Container for buttons stop, play, next, back, and exit
        btn_container = QHBoxLayout()

        self.play = QPushButton('Play')
        self.play.setStyleSheet(btn_style)
        self.play.setCursor(Qt.CursorShape.PointingHandCursor)

        self.prev = QPushButton('Prev')
        self.prev.setStyleSheet(btn_style)
        self.prev.setCursor(Qt.CursorShape.PointingHandCursor)

        self._next = QPushButton('Next')
        self._next.setStyleSheet(btn_style)
        self._next.setCursor(Qt.CursorShape.PointingHandCursor)

        self.stop = QPushButton('Stop')
        self.stop.setCursor(Qt.CursorShape.PointingHandCursor)
        self.stop.setStyleSheet(btn_style)

        self._exit = QPushButton('Exit')
        self._exit.setCursor(Qt.CursorShape.PointingHandCursor)
        self._exit.setStyleSheet('QPushButton{background-color: firebrick;} \
                                 QPushButton:hover{background-color: red; color: white; \
                                 font-weight: bold;}')

        # Add buttons to button container
        btn_container.addWidget(self.play)
        btn_container.addWidget(self.prev)
        btn_container.addWidget(self._next)
        btn_container.addWidget(self.stop)
        btn_container.addWidget(self._exit)

        # Placeholder
        holder = QFrame()
        holder.setFrameShape(QFrame.Shape.Box)
        holder.setFrameShadow(QFrame.Shadow.Sunken)
      

        # Volume Controller - Creating the box give the light gray square
        dialbox = QVBoxLayout()
        dialframe = QFrame()
        dialframe.setFrameShape(QFrame.Shape.Box)
        dialframe.setFrameShadow(QFrame.Shadow.Sunken)
        dialframe.setLayout(dialbox)

        # QDial creates the object, setRange - the range for volume
        # setNotchesVisible - the ticks around the dial
        # setSliderPosition sets the default volume
        # setValues is used for passing variables
        self.dial = QDial()
        self.dial.setRange(0, 100)
        self.dial.setNotchesVisible(True)
        self.dial.setSliderPosition(50)
        self.dial.setValue(50)

        # The volume label in with the dial control
        dialbox.addWidget(QLabel('Volume'))
        dialbox.addWidget(self.dial)
              
        # section for adding widgets to main container
        container.addWidget(header, 0, 0, 1, 3, Qt.AlignmentFlag.AlignTop)
        container.addWidget(self.status, 1, 0, 1, 1)
        container.addWidget(self.track, 1, 1, 1, 1)
        container.addLayout(box1, 1, 2, 1, 1)
        container.addWidget(frame, 2, 0, 2, 1)
        container.addWidget(self.music_list, 2, 1, 1, 2)
        container.addLayout(btn_container, 3, 1, 1, 2)
        container.addWidget(holder, 4, 0, 1, 2)
        container.addWidget(dialframe, 4, 2, 1, 1)

        # Sets main layout
        widget = QWidget()
        widget.setLayout(container)
        self.setCentralWidget(widget)


class Controller:
    def __init__(self, window, data):
        self.window = window
        self.data = data

        # Set some variables for our player
        self.url = QUrl
        self.player = QMediaPlayer()
        self.playlist = QMediaPlaylist(self.player)
        self.player.setPlaylist(self.playlist)

        # Button Commands
        self.window.file_button.released.connect(self.getfiles)
        self.window.clear_button.released.connect(self.clear)
        self.window.play.released.connect(self.state)
        self.window.prev.released.connect(self.prev)
        self.window._next.released.connect(self._next)
        self.window._exit.released.connect(sys.exit)
        self.window.stop.released.connect(self.stop)
      
        # Other commands
        self.playlist.currentIndexChanged.connect(self.update)
        self.window.music_list.itemDoubleClicked.connect(self.doubleclick)
        self.window.dial.valueChanged.connect(self.volume, self.window.dial.value())
        self.player.metaDataChanged.connect(self.meta)

    def meta(self):
        if self.player.isMetaDataAvailable():
            data = self.data.data(self.player)
            self.window.artist.setText(data['artist'])
            self.window.album.setText(self.truncate(data['album'].strip('(')))
            self.window._track.setText(self.truncate(data['track'].strip('(')))
            self.window.release.setText(str(data['release']))
            self.window.genre.setText(data['genre'])

            pixmap = QPixmap(data['coverart'])
            pixmap = pixmap.scaled(380,320)
            self.window.art.setPixmap(pixmap)

            self.window.track.setText(f'Track: {self.truncate(data['track'])}')

    def volume(self, val=50):
        self.player.setVolume(val)

    def getfiles(self):
        ''' Method for getting our audio files - Can store multiple select ions '''
        files = QFileDialog.getOpenFileNames(None, 'Get Audio Giles',
                                             filter='Audio Files(*.mp3 *.ogg *.wav)')
      
        # Loop through the files and add them to our playlist
        for file in files[0]:
            self.playlist.addMedia(QMediaContent(self.url.fromLocalFile(file)))
            try:
                track = MP3(file)
                self.window.music_list.addItem(str(track['TIT2']))
            except:
                track = self.truncate(file.rpartition('/')[2].rpartition('.')[0])
                # Adds the track title to our listbox
                self.window.music_list.addItem(track)

        # Set indexes in our playlist and listbox to 0
        self.window.music_list.setCurrentRow(0)
        self.playlist.setCurrentIndex(0)
      
    def truncate(self, text, length=25):
        # method shortens the text track a little
        if text:
            if len(text) <= length:
                return text
            else:
                return f"{' '.join(text[:length+1].split( ' ')[0:-1])} ..."

    def clear(self):
        ''' Method clears everthing. More will be added here later. '''
        self.player.stop()
        self.window.music_list.clear()
        self.playlist.clear()
        self.window.play.setText('Play')
        self.window.artist.setText('')
        self.window.album.setText('')
        self.window._track.setText('')
        self.window.release.setText('')
        self.window.genre.setText('')
        self.window.art.setPixmap(QPixmap())
      
    def state(self):
        ''' Method checks the state of the music player and append correct text
            to the play button '''
        if self.playlist.mediaCount() > 0:
            if self.player.state() != QMediaPlayer.PlayingState:
                self.window.play.setText('Paused')
                self.window.status.setText('Status: Now Playing')
                self.player.play()
            else:
                self.window.play.setText('Play')
                self.window.status.setText('Status: Now Paused')
                self.player.pause()
          
    def _next(self):
        ''' method for moving to next track '''
        self.playlist.next()
        if self.playlist.currentIndex() == -1:
            self.playlist.setCurrentIndex(0)
            self.player.play()
  
    def prev(self):
        ''' Method for going to previous track '''
        if self.playlist.previousIndex() == -1:
            self.playlist.setCurrentIndex(self.playlist.mediaCount()-1)
        else:
            self.playlist.previous()
          
    def stop(self):
        ''' method stops music player and resets indexes. '''
        self.player.stop()
        self.window.play.setText('Play')
        self.playlist.setCurrentIndex(0)
        self.window.music_list.setCurrentRow(0)

    def update(self):
        ''' Method updates our list box highlight with the playlist '''
        self.window.music_list.setCurrentRow(self.playlist.currentIndex())
        if self.playlist.currentIndex() < 0:
            self.window.music_list.setCurrentRow(0)
            self.playlist.setCurrentIndex(0)

    def doubleclick(self):
        ''' method plays a track that has been double clicked in the listbox .'''
        self.playlist.setCurrentIndex(self.window.music_list.currentRow())
        self.player.play()

if __name__ == '__main__':
    app = QApplication([])
    controller = Controller(Window(), Data())
    controller.window.show()
    sys.exit(app.exec())

To view this content we will need your consent to set third party cookies.
For more detailed information, see our cookies page.
Back
Top Bottom