I'm trying to play a sound when a button is pressed in PySide6, but the audio doesn't actually play — even though there's no error. What's going on?
This issue usually comes down to Python's garbage collector quietly cleaning up your audio object before it has a chance to finish playing. Let's walk through how to play sounds in a PySide6 application and how to avoid this problem.
A note on QSound vs. QSoundEffect
In older versions of Qt (and PyQt5/PySide2), there was a class called QSound for playing simple .wav files. In Qt6, QSound has been removed and replaced by QSoundEffect, which lives in the PySide6.QtMultimedia module. QSoundEffect is designed for short, low-latency sound effects — button clicks, notifications, alerts, and similar.
If you need to play longer audio files or formats like .mp3, you'll want to look at QMediaPlayer instead. For this tutorial, we'll focus on QSoundEffect for playing .wav files.
Installing QtMultimedia
The QtMultimedia module may not be included with your base PySide6 install. You can install it with:
pip install PySide6-Multimedia
Getting the import right
A common first mistake is trying to access QtMultimedia as an attribute of the PySide6 module directly. Instead, import the class you need from PySide6.QtMultimedia:
from PySide6.QtMultimedia import QSoundEffect
With this import in place, you can create a QSoundEffect object and tell it to play.
The silent garbage collection problem
Here's where things get subtle. Say you write a slot (a method connected to a button click) like this:
def play_sound(self):
audio = QSoundEffect()
audio.setSource(QUrl.fromLocalFile("alert.wav"))
audio.play()
You run the code, press the button, and... nothing happens. No error, no crash, just silence.
The reason is that audio is a local variable. As soon as play_sound() finishes executing, Python's garbage collector sees that nothing else references the QSoundEffect object and deletes it. The sound never gets a chance to actually play.
This kind of bug is tricky because there's no error message — the object just disappears silently.
The fix: keep a reference
The solution is to make sure the QSoundEffect object stays alive long enough to play. There are two good ways to do this:
Store it as an instance variable
By assigning the object to self, it lives as long as your widget does:
def play_sound(self):
self.audio = QSoundEffect()
self.audio.setSource(QUrl.fromLocalFile("alert.wav"))
self.audio.play()
Even better, create the QSoundEffect once in __init__ and just call play() when needed. This avoids recreating the object every time the button is pressed:
def __init__(self):
super().__init__()
self.audio = QSoundEffect()
self.audio.setSource(QUrl.fromLocalFile("alert.wav"))
def play_sound(self):
self.audio.play()
Pass a parent object
Qt objects can be given a parent, which keeps them alive as long as the parent exists. Passing self (the widget) as the parent achieves this:
def play_sound(self):
audio = QSoundEffect(self)
audio.setSource(QUrl.fromLocalFile("alert.wav"))
audio.play()
Both approaches work. Storing the object in __init__ is generally cleaner, especially if you're playing the same sound repeatedly.
A general rule for Qt objects in Python
This garbage collection behavior isn't specific to sound — it applies to any Qt object. If you create a QWidget, QTimer, QSoundEffect, or any other QObject as a local variable without giving it a parent or storing a reference, Python may delete it before it does anything useful.
The rule of thumb: always keep a reference to Qt objects you want to stick around, either by storing them on self or by assigning a parent. If you're new to PySide6, our guide to creating your first window covers these fundamentals in more detail.
Complete working example
Here's a full example that plays a .wav file when you click a button. Make sure you have a file called alert.wav in the same directory as your script (any short .wav file will do).
import sys
from PySide6.QtCore import QUrl
from PySide6.QtWidgets import (
QApplication,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget,
)
from PySide6.QtMultimedia import QSoundEffect
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Sound Player")
# Set up the sound effect once.
self.sound = QSoundEffect()
self.sound.setSource(QUrl.fromLocalFile("alert.wav"))
self.sound.setVolume(0.5) # Volume from 0.0 to 1.0
# Create a button to trigger the sound.
button = QPushButton("Play Sound")
button.clicked.connect(self.play_sound)
layout = QVBoxLayout()
layout.addWidget(button)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
def play_sound(self):
self.sound.play()
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
Click the button, and you should hear your sound file play. Because self.sound is stored as an instance variable on the window, it won't be garbage collected — it will stay alive for as long as the window exists. The button uses a signal and slot connection to trigger playback when clicked.
Playing MP3 or other formats with QMediaPlayer
QSoundEffect only supports .wav files. If you need to play .mp3, .ogg, or other formats, use QMediaPlayer with QAudioOutput instead:
import sys
from PySide6.QtCore import QUrl
from PySide6.QtWidgets import (
QApplication,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget,
)
from PySide6.QtMultimedia import QAudioOutput, QMediaPlayer
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Music Player")
# Set up the media player.
self.audio_output = QAudioOutput()
self.player = QMediaPlayer()
self.player.setAudioOutput(self.audio_output)
self.player.setSource(QUrl.fromLocalFile("song.mp3"))
self.audio_output.setVolume(0.5)
# Create a button to trigger playback.
button = QPushButton("Play Music")
button.clicked.connect(self.play_music)
layout = QVBoxLayout()
layout.addWidget(button)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
def play_music(self):
self.player.play()
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
Notice that both self.player and self.audio_output are stored as instance variables. The same garbage collection principle applies — if either object gets collected, playback will fail silently. For a complete multimedia application example, take a look at our Python multimedia player.
Summary
- Use
QSoundEffectfromPySide6.QtMultimediafor short.wavsound effects. - Use
QMediaPlayerwithQAudioOutputfor longer audio or formats like.mp3. - Always store your audio objects as instance variables (on
self) or give them a parent widget. Otherwise, Python's garbage collector may delete them before they finish playing — with no error message to tell you what went wrong.
Once you're ready to distribute your application, see our guide on packaging PySide6 applications with PyInstaller on Windows to share your sound-enabled app with others.
Create GUI Applications with Python & Qt6 by Martin Fitzpatrick — (PyQt6 Edition) The hands-on guide to making apps with Python — Save time and build better with this book. Over 15K copies sold.