1

I have a list of mp3 files that I'm trying to play through pygame whenever different buttons are pressed (one file per button). Since there is a variable number of those files, I'm simply implementing a for loop, and I have an AudioPlayer class that I instantiate each time as follows:

import sys, pygame
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

class AudioPlayer(QObject):
    def __init__(self, filename):
        super().__init__()
        self.filename = filename
        print("Created " + filename)

    def play(self):
        print("Playing " + self.filename)
        pygame.mixer.music.load(self.filename)
        pygame.mixer.music.play()

class Session(QMainWindow):
    def __init__(self):
        super().__init__()
        self.mainWid = QWidget(self)
        self.vbox = QVBoxLayout()
        self.mainWid.setLayout(self.vbox)
        self.setCentralWidget(self.mainWid)
        self.show()

        pygame.mixer.init()

        filenames = [r'C:\...\file1.mp3', r'C:\...\file2.mp3']

        for filename in filenames:
            playButton = QPushButton('Play', self)
            localPlay = AudioPlayer(filename)
            playButton.clicked.connect(localPlay.play)
            self.vbox.addWidget(playButton)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    Session()
    sys.exit(app.exec_())

My problem is simply that the files do not play when I press the button, neither does the message get printed at all; it's like the slot never gets called:

admin@home> python main2.py
Created C:\...\file1.mp3
Created C:\...\file2.mp3
admin@home>

If I play the files manually outside of the loop, like this, it works:

class Session(QMainWindow):
    def __init__(self):

        # ...

        filenames = [r'C:\...\file1.mp3', r'C:\...\file2.mp3']

        pygame.mixer.music.load(filenames[0])
        pygame.mixer.music.play()

As you can see, I made sure AudioPlayer inherited QObject and called __init__, so I believe it should be able to receive signals. So what is going on here? Is it a local variable issue?

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
pie3636
  • 795
  • 17
  • 31

1 Answers1

2

The problem is caused by the fact that the AudioPlayer objects created in the loop are local variables, so when they finish running the constructor they are deleted from memory. There are 2 possible solutions, the first is to make them attributes of the class, or the second is to pass them a parent since they inherit from QObject, I will use this second method:

class AudioPlayer(QObject):
    def __init__(self, filename, parent=None):
        super().__init__(parent=parent)
        self.filename = filename
        print("Created " + filename)

    @pyqtSlot()
    def play(self):
        print("Playing " + self.filename)
        pygame.mixer.music.load(self.filename)
        pygame.mixer.music.play()

class Session(QMainWindow):
    def __init__(self):
        super().__init__()
        self.mainWid = QWidget(self)
        self.vbox = QVBoxLayout()
        self.mainWid.setLayout(self.vbox)
        self.setCentralWidget(self.mainWid)
        self.show()

        pygame.mixer.init()

        filenames = [r'C:\...\file1.mp3', r'C:\...\file2.mp3']

        for filename in filenames:
            playButton = QPushButton('Play', self)
            localPlay = AudioPlayer(filename, self)
            playButton.clicked.connect(localPlay.play)
            self.vbox.addWidget(playButton)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = Session()
    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thanks for your quick answer, this is what I assumed. Just a few questions if you don't mind: 1. Is the decorator necessary here on `play`? What is its function, is it purely indicative? 2. I don't know if this has to do with the fact I'm using Python 3, but `*args` is apparently not defined. If I replace it and simply use `super().__init__(self, parent=parent)`, I still get an error: `RuntimeError: super-class __init__() of type AudioPlayer was never called`. Any ideas why? – pie3636 Jan 21 '18 at 14:42
  • 1
    @pie3636 The second was a typographical error, I corrected it. the decorator is not only indicative, this helps to improve the execution time and memory consumption of the application, to know about it read the following: http://pyqt.sourceforge.net/Docs/PyQt5/signals_slots.html#PyQt5.QtCore.pyqtSlot – eyllanesc Jan 21 '18 at 14:45