Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Playing music with Pyglet and Tkinter in Python

I wanted to create a simple gui with a play and stop button to play an mp3 file in python. I created a very simple gui using Tkinter that consists of 2 buttons (stop and play).

I created a function that does the following:

def playsound () :
    sound = pyglet.media.load('music.mp3')
    sound.play()
    pyglet.app.run()

I added that function as a command to the button play. I also made a different function to stop music:

def stopsound ():
    pyglet.app.exit

I added this function as a command to the second button. But the problem is that when I hit play, python and the gui freeze. I can try to close the window but it does not close, and the stop button is not responsive. I understand that this is because the pyglet.app.run() is executing till the song is over but how exactly do I prevent this? I want the gui to stop the music when I click on the button. Any ideas on where I can find a solution to this?

like image 417
Paul Avatar asked Apr 23 '12 15:04

Paul


People also ask

How do I play music through tkinter?

There are two modules to play sound with the help of tkinter python: pygame : It is a cross-platform module to create games and GUI's. playsound: It is a cross-platform module and its function name is playsound()


2 Answers

You are mixing two UI libraries together - that is not intrinsically bad, but there are some problems. Notably, both of them need a main loop of their own to process their events. TKinter uses it to communicate with the desktop and user-generated events, and in this case, pyglet uses it to play your music.

Each of these loops prevents a normal "top down" program flow, as we are used to when we learn non-GUI programming, and the program should proceed basically with callbacks from the main loops. In this case, in the middle of a Tkinter callback, you put the pyglet mainloop (calling pyglet.app.run) in motion, and the control never returns to the Tkinter library.

Sometimes loops of different libraries can coexist on the same process, with no conflicts -- but of course you will be either running one of them or the other. If so, it may be possible to run each library's mainloop in a different Python thread.

If they can not exist together, you will have to deal with each library in a different process.

So, one way to make the music player to start in another thread could be:

from threading import Thread

def real_playsound () :
    sound = pyglet.media.load('music.mp3')
    sound.play()
    pyglet.app.run()

def playsound():
    global player_thread
    player_thread = Thread(target=real_playsound)
    player_thread.start()

If Tkinter and pyglet can coexist, that should be enough to get your music to start. To be able to control it, however, you will need to implement a couple more things. My suggestion is to have a callback on the pyglet thread that is called by pyglet every second or so -- this callback checks the state of some global variables, and based on them chooses to stop the music, change the file being played, and so on.

like image 100
jsbueno Avatar answered Oct 01 '22 05:10

jsbueno


I would do something like:

import pyglet
from pyglet.gl import *

class main (pyglet.window.Window):
    def __init__ (self):
        super(main, self).__init__(800, 600, fullscreen = False)
        self.button_texture = pyglet.image.load('button.png')
        self.button = pyglet.sprite.Sprite(self.button_texture)

        self.sound = pyglet.media.load('music.mp3')
        self.sound.play()

        self.alive = 1

    def on_draw(self):
        self.render()

    def on_close(self):
        self.alive = 0

    def on_mouse_press(self, x, y, button, modifiers):
        if x > self.button.x and x < (self.button.x + self.button_texture.width):
            if y > self.button.y and y < (self.button.y + self.button_texture.height):
                self.alive = 0

    def on_key_press(self, symbol, modifiers):
        if symbol == 65307: # [ESC]
            self.alive = 0

    def render(self):
        self.clear()
        self.button.draw()
        self.flip()

    def run(self):
        while self.alive == 1:
            self.render()

            # -----------> This is key <----------
            # This is what replaces pyglet.app.run()
            # but is required for the GUI to not freeze
            #
            event = self.dispatch_events()


x = main()
x.run()
like image 39
Torxed Avatar answered Oct 01 '22 05:10

Torxed