Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
PyQt5 Music Player
#9
Thanks Axel_Erfurt. I haven't gotten that error yet.
Here is what I have now. Added a truncate function to shorten text if too long. Added mutagen to get some metadata, mainly for the listbox display. Added a volume control and working slide with tack duration.

from functools import partial
import os
import sys
import mutagen
from PyQt5.QtCore import Qt, QUrl, QDir
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QGridLayout, \
QFrame, QGraphicsDropShadowEffect, QGraphicsView, QGraphicsScene, QLabel, \
QPushButton, QHBoxLayout, QStyle, QListWidget, QFileDialog, QSlider, QVBoxLayout, QDial
from PyQt5.QtGui import QGradient, QFont, QColor, QCursor, QIcon, QPixmap
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QMediaPlaylist, \
QMediaMetaData


class Window(QMainWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setWindowTitle('PyQt5 Music Player')

        # Create some variables
        self.url = QUrl()
        self.player = QMediaPlayer()
        self.playlist = QMediaPlaylist(self.player)
        self.player.setPlaylist(self.playlist)

        # Create the status and track labels
        common_style = '''
                        font-weight: bold;
                        font-size: 10pt;
                       '''
        self.status = QLabel('Status: Now Stopped')
        self.status.setStyleSheet(common_style)
        self.status.setFrameShape(QFrame.Box)
        self.status.setFrameShadow(QFrame.Sunken)

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

        # Labels for the track information
        artist = QLabel('Artist:')
        artist.setStyleSheet(common_style)
        album = QLabel('Album:')
        album.setStyleSheet(common_style)
        track = QLabel('Track:')
        track.setStyleSheet(common_style)
        released = QLabel('Album Release:')
        released.setStyleSheet(common_style)
        genre = QLabel('Genre:')
        genre.setStyleSheet(common_style)

        self.artist = QLabel()
        self.artist.setStyleSheet(common_style)
        self.album_title = QLabel()
        self.album_title.setStyleSheet(common_style)
        self.track_title = QLabel()
        self.track_title.setStyleSheet(common_style)
        self.released = QLabel()
        self.released.setStyleSheet(common_style)
        self.genre = QLabel()
        self.genre.setStyleSheet(common_style)
        self.art = QLabel()
        self.art.setContentsMargins(5, 170, 5, 5)

        # Timer Label
        self._timer = QLabel('Duration: 00:00:00 / 00:00:00')

        # Define and create the listbox
        self.musiclist = QListWidget()
        self.musiclist.setFrameShape(QFrame.Box)
        self.musiclist.setFrameShadow(QFrame.Sunken)
        self.musiclist.setStyleSheet('background-color: snow;')

        # Create some containers
        btn_box = QHBoxLayout()
        btn_box2 = QHBoxLayout()
        slider_box = QVBoxLayout()
        slider_box.setContentsMargins(5,80,5,5)
        dial_box = QVBoxLayout()
        container = QGridLayout()
        info_container = QGridLayout()
        info_container.setSpacing(8)

        frame = QFrame()
        frame.setMinimumWidth(390)
        frame.setFrameShape(QFrame.Box)
        frame.setFrameShadow(QFrame.Sunken)
        frame.setLayout(info_container)

        img_frame = QFrame()
        img_frame.setMinimumHeight(100)

        # Create slider frame and control
        slider_frame = QFrame()
        slider_frame.setFrameShape(QFrame.Box)
        slider_frame.setFrameShadow(QFrame.Sunken)
        slider_frame.setMinimumHeight(30)
        slider_frame.setLayout(slider_box)

        self.slider = QSlider(Qt.Horizontal)
        slider_box.addWidget(self._timer)
        slider_box.addWidget(self.slider)

        # Create volume frame and control
        dial_frame = QFrame()
        dial_frame.setFrameShape(QFrame.Box)
        dial_frame.setFrameShadow(QFrame.Sunken)
        dial_frame.setLayout(dial_box)

        self.dial = QDial()
        self.dial.setRange(0, 100)
        self.dial.setNotchesVisible(True)
        self.dial.setSliderPosition(70)
        self.dial.setValue(70)
        self.dial.setMinimumWidth(200)
        self.dial.setMaximumWidth(200)
        self.dial.setMinimumHeight(100)
        dial_box.addWidget(QLabel('Volume'))
        dial_box.addWidget(self.dial)

        # Used to update various aspects of gui
        self.playlist.currentIndexChanged.connect(self.update)
        self.dial.valueChanged.connect(self._volume, self.dial.value())
        self.player.metaDataChanged.connect(self.meta_data)
        self.musiclist.itemDoubleClicked.connect(self._doubleclick)
        self.player.positionChanged.connect(self.track_position)
        self.player.durationChanged.connect(self.duration)
        self.slider.valueChanged.connect(self.timer)

        # Add track information to the info container
        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_title, 1, 1, 1, 1)
        info_container.addWidget(track, 2, 0, 1, 1)
        info_container.addWidget(self.track_title, 2, 1, 1, 1)
        info_container.addWidget(released, 3, 0, 1, 1)
        info_container.addWidget(self.released, 3, 1, 1, 1)
        info_container.addWidget(genre, 4, 0, 1, 1)
        info_container.addWidget(self.genre, 4, 1, 1, 1)
        info_container.addWidget(self.art, 5, 0, 1, 2)

        # Create the control buttons & button styles
        btn_style = '''QPushButton{background-color: skyblue;}
                       QPushButton:hover{background-color: lightskyblue; color: dodgerblue; \
                       font-weight: bold;}'''

        # Create buttons for getting audio files and clearing playlist
        file_btn = QPushButton('Get Audio')
        file_btn.released.connect(self._files)
        file_btn.setCursor(Qt.PointingHandCursor)
        file_btn.setStyleSheet(btn_style)
        file_btn.setMaximumWidth(100)

        clear_btn = QPushButton('Clear List')
        clear_btn.released.connect(self._clear)
        clear_btn.setCursor(Qt.PointingHandCursor)
        clear_btn.setStyleSheet(btn_style)
        clear_btn.setMaximumWidth(100)

        # Create & style the control buttons
        self.play_btn = QPushButton('Play')
        self.play_btn.released.connect(self._state)
        self.play_btn.setCursor(Qt.PointingHandCursor)
        self.play_btn.setStyleSheet(btn_style)

        self.prev_btn = QPushButton('Prev')
        self.prev_btn.released.connect(self._prev)
        self.prev_btn.setCursor(Qt.PointingHandCursor)
        self.prev_btn.setStyleSheet(btn_style)

        self.next_btn = QPushButton('Next')
        self.next_btn.released.connect(self._next)
        self.next_btn.setCursor(Qt.PointingHandCursor)
        self.next_btn.setStyleSheet(btn_style)

        self.stop_btn = QPushButton('Stop')
        self.stop_btn.released.connect(self._stop)
        self.stop_btn.setCursor(Qt.PointingHandCursor)
        self.stop_btn.setStyleSheet(btn_style)

        self.exit_btn = QPushButton('Exit')
        self.exit_btn.released.connect(sys.exit)
        self.exit_btn.setCursor(Qt.PointingHandCursor)
        self.exit_btn.setStyleSheet('QPushButton{background-color: firebrick;} \
                                    QPushButton:hover{background-color: red; color: white; \
                                    font-weight: bold;}')


        # Add the buttons to layout
        btn_box.addWidget(file_btn)
        btn_box.addWidget(clear_btn)
        btn_box2.addWidget(self.play_btn)
        btn_box2.addWidget(self.prev_btn)
        btn_box2.addWidget(self.next_btn)
        btn_box2.addWidget(self.stop_btn)
        btn_box2.addWidget(self.exit_btn)


        # Add layouts to container layout
        container.addWidget(self._header_footer(100, 100, 40, 'PyQt5 Music Player'), 0, 0, 1, 3)
        container.addWidget(self.status, 1, 0, 1, 1)
        container.addWidget(self.track, 1, 1, 1, 1)
        container.addLayout(btn_box, 1, 2, 1, 1)
        container.addWidget(frame, 2, 0, 2, 1)
        container.addWidget(self.musiclist, 2, 1, 1, 2)
        container.addLayout(btn_box2, 3, 1, 1, 2)
        container.addWidget(slider_frame, 4, 0, 1, 2)
        container.addWidget(dial_frame, 4, 2, 1, 1)
        container.addWidget(self._header_footer(40, 40, 10, 'my-python.org - 10/16/2021'), 5, 0, 1, 3)


        # Create the widget and set layout to container
        widget = QWidget()
        widget.setLayout(container)
        self.setCentralWidget(widget)

    # Volume control
    def _volume(self, val=70):
        self.player.setVolume(val)

    # Sets position of slider
    def track_position(self, position):
        self.slider.setValue(position)

    # Duration of the track playing
    def duration(self, duration):
        self.slider.setRange(0, duration)

    # Updates duration of track playing
    def timer(self):
        total_milliseconds = self.player.duration()
        total_seconds, total_milliseconds = divmod(total_milliseconds,1000)
        total_minutes, total_seconds = divmod(total_seconds,60)
        total_hours, total_minutes = divmod(total_minutes, 60)

        elapsed_milliseconds = self.slider.value()
        elapsed_seconds, elapsed_milliseconds = divmod(elapsed_milliseconds,1000)
        elapsed_minutes, elapsed_seconds = divmod(elapsed_seconds, 60)
        elapsed_hours, elapsed_minutes = divmod(elapsed_minutes, 60)

        self._timer.setText(f'Duration: {elapsed_hours:02d}:{elapsed_minutes:02d}:{elapsed_seconds:02d} / {total_hours:02d}:{total_minutes:02d}:{total_seconds:02d}')

    # Shorten text that may be too long
    def _truncate(self, text, length=25):
        if len(text) <= length:
            return text
        else:
            return f"{' '.join(text[:length+1].split(' ')[0:-1])} ...."

    # Get music metadata
    def meta_data(self):
        if self.player.isMetaDataAvailable():
            self.artist.setText(self.player.metaData(QMediaMetaData.AlbumArtist))
            self.album_title.setText(self._truncate(self.player.metaData(QMediaMetaData.AlbumTitle)))
            self.track_title.setText(self._truncate(self.player.metaData(QMediaMetaData.Title)))
            self.released.setText(f'{self.player.metaData(QMediaMetaData.Year)}')
            self.genre.setText(self.player.metaData(QMediaMetaData.Genre))
            self.track.setText(f'Track: {self._truncate(self.player.metaData(QMediaMetaData.Title),20)}')
            if self.player.metaData(QMediaMetaData.CoverArtImage):
                pixmap = QPixmap(self.player.metaData(QMediaMetaData.CoverArtImage))
                pixmap = pixmap.scaled(int(pixmap.width()/3), int(pixmap.height()/3))
                self.art.setPixmap(pixmap)
                self.art.setContentsMargins(0,32 , 0, 5)

    # Create the header
    def _header_footer(self, minheight, maxheight, fontsize, text):
        shadow = QGraphicsDropShadowEffect()
        shadow.setBlurRadius(3)
        shadow.setOffset(3, 3)

        scene = QGraphicsScene()

        view = QGraphicsView()
        view.setMinimumSize(800, minheight)
        view.setMaximumHeight(maxheight)
        view.setScene(scene)

        gradient = QGradient(QGradient.RichMetal)

        scene.setBackgroundBrush(gradient)

        font = QFont('comic sans ms', fontsize, QFont.Bold)

        text = scene.addText(text)
        text.setDefaultTextColor(QColor(250,250,250))
        text.setFont(font)

        text.setGraphicsEffect(shadow)

        return view

    # Method for double clicking a track and play
    def _doubleclick(self):
        self.playlist.setCurrentIndex(self.musiclist.currentRow())
        self.player.play()

    # Method for clearing the playlist and musiclist
    def _clear(self):
        self.player.stop()
        self.musiclist.clear()
        self.playlist.clear()
        self.play_btn.setText('Play')
        self.status.setText('Status: ')
        self.track.setText('Track: ')
        self.artist.setText('Artist: ')
        self.album_title.setText('Album: ')
        self.track_title.setText('Track: ')
        self.released.setText('Released: ')
        self.genre.setText('Genre: ')
        self.art.setContentsMargins(5, 170, 5, 50)
        pixmap = QPixmap()
        self.art.setPixmap(pixmap)
        self.dial.setSliderPosition(70)
        self.dial.setValue(70)

    # Method for adding tracks to the playlist and musiclist
    def _files(self):
        files = QFileDialog.getOpenFileNames(None, 'Get Audio Files',
                                         filter='Audio Files (*.mp3 *.ogg *.wav)')
        for file in files[0]:
            self.playlist.addMedia(QMediaContent(self.url.fromLocalFile(file)))
            track = mutagen.File(file)
            self.musiclist.addItem(str(track['TIT2']))

        self.musiclist.setCurrentRow(0)
        self.playlist.setCurrentIndex(0)

    # Methods for the control buttons
    def _prev(self):
        if self.playlist.previousIndex() == -1:
            self.playlist.setCurrentIndex(self.playlist.mediaCount()-1)
        else:
            self.playlist.previous()

    def _next(self):
        self.playlist.next()
        if self.playlist.currentIndex() == -1:
            self.playlist.setCurrentIndex(0)
            self.player.play()

    def _stop(self):
        self.player.stop()
        self.play_btn.setText('Play')
        self.playlist.setCurrentIndex(0)
        self.musiclist.setCurrentRow(0)
        self.status.setText('Status: Now Stopped')

    def _state(self):
        if self.playlist.mediaCount() > 0:
            if self.player.state() != QMediaPlayer.PlayingState:
                self.play_btn.setText('Pause')
                self.status.setText('Status: Now Playing')
                self.player.play()
            else:
                self.play_btn.setText('Play')
                self.player.pause()
                self.status.setText('Status: Now Paused')

        else:
            pass

    # Method for updating the listbox when the playlist updates
    def update(self):
        self.musiclist.setCurrentRow(self.playlist.currentIndex())
        if self.playlist.currentIndex() < 0:
            self.musiclist.setCurrentRow(0)
            self.playlist.setCurrentIndex(0)

def main():
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec())

if __name__ == '__main__':
    main()
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
PyQt5 Music Player - by menator01 - Oct-14-2021, 09:38 AM
RE: PyQt5 Music Player - by Larz60+ - Oct-14-2021, 08:49 PM
RE: PyQt5 Music Player - by Axel_Erfurt - Oct-14-2021, 09:16 PM
RE: PyQt5 Music Player - by menator01 - Oct-15-2021, 05:58 AM
RE: PyQt5 Music Player - by menator01 - Oct-16-2021, 09:44 PM
RE: PyQt5 Music Player - by Axel_Erfurt - Oct-16-2021, 10:49 PM
RE: PyQt5 Music Player - by menator01 - Oct-16-2021, 11:00 PM
RE: PyQt5 Music Player - by Axel_Erfurt - Oct-17-2021, 09:26 PM
RE: PyQt5 Music Player - by menator01 - Oct-17-2021, 10:03 PM
RE: PyQt5 Music Player - by Axel_Erfurt - Oct-18-2021, 04:09 PM
RE: PyQt5 Music Player - by Axel_Erfurt - Oct-18-2021, 05:12 PM
RE: PyQt5 Music Player - by menator01 - Oct-18-2021, 06:44 PM
RE: PyQt5 Music Player - by Axel_Erfurt - Oct-19-2021, 05:21 PM
RE: PyQt5 Music Player - by menator01 - Oct-29-2021, 06:49 PM
RE: PyQt5 Music Player - by menator01 - Nov-02-2021, 09:34 AM
RE: PyQt5 Music Player - by Axel_Erfurt - Nov-02-2021, 08:04 PM
RE: PyQt5 Music Player - by menator01 - Nov-03-2021, 08:30 AM
RE: PyQt5 Music Player - by Axel_Erfurt - Nov-03-2021, 09:05 AM
RE: PyQt5 Music Player - by menator01 - Nov-03-2021, 09:06 AM
RE: PyQt5 Music Player - by Axel_Erfurt - Nov-03-2021, 09:11 AM
RE: PyQt5 Music Player - by menator01 - Nov-03-2021, 05:49 PM
RE: PyQt5 Music Player - by Axel_Erfurt - Nov-03-2021, 05:59 PM

Possibly Related Threads…
Thread Author Replies Views Last Post
  Basic Music Player with tkinter menator01 4 4,774 Jul-31-2021, 04:27 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