You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyQt6音乐播放器切换曲目崩溃问题求助

解决PyQt6音乐播放器曲目切换崩溃问题

问题根源

  1. 资源冲突:直接在播放状态下切换曲目,旧音频流未终止就加载新源,导致底层资源占用冲突,触发崩溃。
  2. 重复信号连接:每次播放时重复绑定durationChanged到initSlider,多次触发会导致滑块逻辑混乱,加剧状态异常。
  3. 状态不同步:未处理播放器在播放状态下的切换流程,内部状态未重置就加载新曲目,引发逻辑错误。

修复方案

  1. 切换曲目前先调用stop()终止当前播放,确保旧资源释放后再加载新曲目。
  2. 将durationChanged信号的绑定移到初始化阶段,只执行一次连接。
  3. 增加空值判断,避免因列表为空或项目不存在导致的索引错误。

修改后的完整代码

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.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)
        # 仅初始化时绑定一次信号
        self.player.durationChanged.connect(self.initSlider)

    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.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()
        if not item or not self.playlist:
            return
        index = self.playlist_widget.row(item)
        if self.player.playbackState() == QMediaPlayer.PlaybackState.PausedState:
            self.player.play()
        else:
            url = QUrl.fromLocalFile(self.playlist[index])
            # 切换前先终止当前播放
            self.player.stop()
            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.stop()
            self.player.setSource(url)
            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)

    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())

关键修改说明

  • 把durationChanged信号的绑定移到initPlayer方法中,避免重复绑定导致的逻辑混乱。
  • 在play_music和play_selected_track中,切换曲目前调用stop()释放旧音频资源,解决资源冲突问题。
  • 增加空项目和空列表的判断,防止因无效索引引发的错误。

内容的提问来源于stack exchange,提问作者RD60

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 07:55:58