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

PyQt6使用QAudioOutput与QMediaPlayer实现音频淡入淡出效果求助

解决PyQt音频淡入淡出UI阻塞问题

核心思路

之前的实现方式出现UI阻塞,本质是音量调整逻辑阻塞了主线程的事件循环。使用Qt内置的QPropertyAnimation可以无阻塞实现属性平滑过渡,完全不会影响UI响应。

修改后完整代码

import sys

from PyQt6 import QtWidgets
from PyQt6.QtCore import QUrl, QPropertyAnimation
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QPushButton, QStyle


class MainWindowUi(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self._setup_ui()

        self.setWindowTitle('sound fading')
        self.setGeometry(100, 100, 100, 100)

    def _setup_ui(self):
        self.central_widget = QWidget(self)
        self.central_widget_layout = QVBoxLayout(self.central_widget)
        self.setCentralWidget(self.central_widget)

        self.player = AudioPlayer()
        self.user_action = -1  # 0 - stopped, 1 - playing, 2 - paused
        self.play_button = QPushButton()
        self.play_button.clicked.connect(self.play_pause_button_clicked)

        self.play_icon = self.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)
        self.pause_icon = self.style().standardIcon(QStyle.StandardPixmap.SP_MediaPause)
        self.play_button.setIcon(self.play_icon)

        self.central_widget_layout.addWidget(self.play_button)

    def play(self):
        print("Play")
        self.play_button.setIcon(self.pause_icon)
        self.user_action = 1
        # 替换为实际音频文件路径
        self.player.setSource(QUrl.fromLocalFile("你的音频文件路径.mp3"))
        self.player.play()

    def pause(self):
        print("Pause")
        self.play_button.setIcon(self.play_icon)
        self.user_action = 2
        self.player.pause()

    def unpause(self):
        print("Unpause")
        self.play_button.setIcon(self.pause_icon)
        self.user_action = 1
        self.player.play()

    def play_pause_button_clicked(self):
        if self.user_action <= 0:
            self.play()
        elif self.user_action == 1:
            self.pause()
        elif self.user_action == 2:
            self.unpause()


class AudioPlayer(QMediaPlayer):
    def __init__(self, parent=None, fade_duration=600):
        super().__init__(parent)
        self.audio_output = QAudioOutput()
        self.setAudioOutput(self.audio_output)
        self.target_volume = 0.3  # 最终播放音量
        self.audio_output.setVolume(self.target_volume)
        self.fade_duration = fade_duration  # 淡入淡出时长,单位毫秒
        # 初始化音量动画对象,绑定音频输出的volume属性
        self.volume_anim = QPropertyAnimation(self.audio_output, b"volume")
        self.volume_anim.setDuration(self.fade_duration)

    def play(self):
        # 停止正在运行的动画,避免状态切换冲突
        self.volume_anim.stop()
        # 淡入逻辑:先将音量置0,再平滑升到目标音量
        self.audio_output.setVolume(0)
        super().play()
        self.volume_anim.setStartValue(0)
        self.volume_anim.setEndValue(self.target_volume)
        self.volume_anim.start()

    def pause(self):
        self.volume_anim.stop()
        # 淡出逻辑:从当前音量平滑降到0,动画结束后再执行暂停
        self.volume_anim.setStartValue(self.audio_output.volume())
        self.volume_anim.setEndValue(0)
        self.volume_anim.finished.connect(self._finish_pause)
        self.volume_anim.start()

    def _finish_pause(self):
        super().pause()
        # 重置音量为目标值,方便下次播放时淡入
        self.audio_output.setVolume(self.target_volume)
        # 断开信号绑定,避免后续动画重复触发该方法
        self.volume_anim.finished.disconnect(self._finish_pause)


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    mainWindow = MainWindowUi()
    mainWindow.show()

    sys.exit(app.exec())

实现说明

  • 所有动画逻辑由Qt原生事件循环调度,全程不阻塞主线程,播放/暂停按钮可随时点击响应
  • 默认淡入淡出时长为600毫秒,可通过修改AudioPlayer初始化时的fade_duration参数自定义时长
  • 每次启动新动画前会停止正在运行的动画,避免用户快速切换播放状态时出现音量异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 10:15:00