I load playlist by clicking "Add to playlist". When I click on any item in the list and player works fine. But if i try to click another track in the list, the program crashes. I have noticed the following factors:
I tried to fix it but it didn't work. Thank you in advance.
import os
import sys
from PyQt6.QtCore import Qt, QUrl, QTimer
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput, QAudio
from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QSlider, QVBoxLayout, QHBoxLayout, QLabel, QFileDialog, \
QListWidget, QListWidgetItem
class MusicPlayer(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Music Player")
self.setGeometry(100, 100, 400, 200)
self.initUI()
self.initPlayer()
self.initSlider()
self.initTimer()
def initTimer(self):
self.tmr0 = QTimer()
self.tmr0.timeout.connect(self.on_time)
# self.tmr0.setInterval(5000)
self.tmr0.start(1)
def on_time(self):
if not self.position_slider.isSliderDown():
pos = self.player.position()
self.position_slider.setSliderPosition(pos)
def initSlider(self):
self.position_slider.setMaximum(self.player.duration())
self.position_slider.setToolTip("Позиция трека")
def initPlayer(self):
self.player = QMediaPlayer()
self.playlist = []
self.audio_output = QAudioOutput()
self.player.setAudioOutput(self.audio_output)
self.saved_volume = 0.5
self.audio_output.setVolume(self.saved_volume)
def initUI(self):
self.play_button = QPushButton("Play")
self.play_button.clicked.connect(self.play_music)
self.pause_button = QPushButton("Pause")
self.pause_button.clicked.connect(self.pause_music)
self.stop_button = QPushButton("Stop")
self.stop_button.clicked.connect(self.stop_music)
self.volume_slider = QSlider(Qt.Orientation.Horizontal)
self.volume_slider.setValue(50)
self.volume_slider.setMinimum(0)
self.volume_slider.setMaximum(100)
self.volume_slider.setToolTip("Volume")
self.volume_slider.valueChanged.connect(self.set_volume)
self.position_slider = QSlider(Qt.Orientation.Horizontal)
self.position_slider.setToolTip("Position")
# self.position_slider.sliderMoved.connect(self.set_position)
self.position_slider.sliderReleased.connect(self.on_slider_release)
self.backward_button = QPushButton("<< 10 sec")
self.backward_button.clicked.connect(self.backward_music)
self.forward_button = QPushButton("10 sec >>")
self.forward_button.clicked.connect(self.forward_music)
self.select_file_button = QPushButton("Add to playlist")
self.select_file_button.clicked.connect(self.select_file)
self.clear_playlist_button = QPushButton("Clear playlist")
self.clear_playlist_button.clicked.connect(self.clear_playlist)
self.playlist_widget = QListWidget()
self.playlist_widget.itemDoubleClicked.connect(self.play_selected_track)
self.track_label = QLabel("Track name")
vbox = QVBoxLayout()
hbox1 = QHBoxLayout()
hbox2 = QHBoxLayout()
hbox3 = QHBoxLayout()
hbox1.addWidget(self.play_button)
hbox1.addWidget(self.pause_button)
hbox1.addWidget(self.stop_button)
hbox1.addWidget(self.volume_slider)
hbox2.addWidget(self.backward_button)
hbox2.addWidget(self.position_slider)
hbox2.addWidget(self.forward_button)
hbox3.addWidget(self.track_label)
hbox3.addWidget(self.select_file_button)
hbox3.addWidget(self.clear_playlist_button)
vbox.addWidget(self.playlist_widget)
vbox.addLayout(hbox3)
vbox.addLayout(hbox1)
vbox.addLayout(hbox2)
self.setLayout(vbox)
def select_file(self):
folder_path = QFileDialog.getExistingDirectory(self, "Выбрать папку с музыкальными файлами", "")
if folder_path:
files = [os.path.join(folder_path, file) for file in os.listdir(folder_path) if
file.endswith(('.mp3', '.wav', '.ogg'))]
file_names = [file for file in os.listdir(folder_path) if file.endswith(('.mp3', '.wav', '.ogg'))]
for file in files:
self.playlist.append(file)
for file_name in file_names:
item = QListWidgetItem(file_name)
self.playlist_widget.addItem(item)
def play_music(self):
item = self.playlist_widget.currentItem()
index = self.playlist_widget.row(item)
if self.player.playbackState() == QMediaPlayer.PlaybackState.PausedState:
self.player.play()
else:
if (self.playlist):
url = QUrl.fromLocalFile(self.playlist[index]) # Создаем объект QUrl из строки с путем к файлу
self.player.durationChanged.connect(self.initSlider)
self.player.setSource(url)
self.audio_output.setVolume(self.saved_volume)
self.player.play()
def play_selected_track(self, item):
index = self.playlist_widget.row(item)
if 0 <= index < len(self.playlist):
url = QUrl.fromLocalFile(self.playlist[index])
self.player.setSource(url)
self.player.durationChanged.connect(self.initSlider)
self.audio_output.setVolume(self.saved_volume)
self.player.play()
def pause_music(self):
self.player.pause()
def stop_music(self):
self.player.stop()
def set_volume(self):
volume = self.volume_slider.value()
self.saved_volume = self.real_volume(volume)
self.audio_output.setVolume(self.saved_volume)
"""Метод конвертации значения слайдера (от 0 до 100)
к виду (от 0.0 до 1.0) и преобразование шкалы громкости
в логарифмическую(удобную для восприятия человеком)"""
def real_volume(self, slider_value):
return QAudio.convertVolume(
slider_value * .01,
QAudio.VolumeScale.LogarithmicVolumeScale,
QAudio.VolumeScale.LinearVolumeScale
)
def backward_music(self):
position_m = max(0, self.player.position() - 10000)
self.player.setPosition(position_m)
def forward_music(self):
position_m = min(self.player.duration(), self.player.position() + 10000)
self.player.setPosition(position_m)
def on_slider_release(self):
self.player.setPosition(self.position_slider.value())
def clear_playlist(self):
self.playlist_widget.clear()
self.playlist.clear()
app = QApplication(sys.argv)
playerM = MusicPlayer()
playerM.show()
sys.exit(app.exec())
I thought it might be necessary to reset Source, before i select new track, but I didn't find the command. I want to change tracks without crashes. Also I've noticed it crashed on line 180: self.player.setSource(url)
I've found some kind of solution. Before I set new source to play I stop the player, then I check if player is stopped. After that I call a function with a delay using QTimer.singleShot
self.player.stop()
if self.player.playbackState() == QMediaPlayer.PlaybackState.StoppedState:
QTimer.singleShot(1, lambda: self.source_setter(url))
def source_setter(self, url):
self.player.setSource(url)
self.player.play()