wxPython: Creating a Simple Media Player

A few months ago, I wrote about creating a simple MP3 Player using wxPython’s MediaCtrl widget. Since then, a fellow released the MplayerCtrl, a wxPython widget that wraps mplayer, a popular cross-platform media player. I actually ended up switching my MP3 Player’s backend to use this new control, but that’s a story for another post. This article will just focus on creating a really simple Media Player that you can play movies with. And you can do it all with Python! If you’re like me, you’ll think this rocks!

Playing Movies with Python & Mplayer


Screenshot is from a trailer for Shorts

First off, you’ll need to make sure you have the following:

Once you’ve confirmed that you have all that, we can continue. Let’s look at some code!

 
import os
import time
import wx
import MplayerCtrl as mpc
import wx.lib.buttons as buttons

dirName = os.path.dirname(os.path.abspath(__file__))
bitmapDir = os.path.join(dirName, 'bitmaps')

class Frame(wx.Frame):
    
    #----------------------------------------------------------------------
    def __init__(self, parent, id, title, mplayer):
        wx.Frame.__init__(self, parent, id, title)
        self.panel = wx.Panel(self)
        
        sp = wx.StandardPaths.Get()
        self.currentFolder = sp.GetDocumentsDir()
        self.currentVolume = 50

        self.create_menu()
        
        # create sizers
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        controlSizer = self.build_controls()
        sliderSizer = wx.BoxSizer(wx.HORIZONTAL)
        
        self.mplayer = mpc.MplayerCtrl(self.panel, -1, mplayer)
        self.playbackSlider = wx.Slider(self.panel, size=wx.DefaultSize)
        sliderSizer.Add(self.playbackSlider, 1, wx.ALL|wx.EXPAND, 5)
        
        # create volume control
        self.volumeCtrl = wx.Slider(self.panel)
        self.volumeCtrl.SetRange(0, 100)
        self.volumeCtrl.SetValue(self.currentVolume)
        self.volumeCtrl.Bind(wx.EVT_SLIDER, self.on_set_volume)
        controlSizer.Add(self.volumeCtrl, 0, wx.ALL, 5)

        # create track counter
        self.trackCounter = wx.StaticText(self.panel, label="00:00")
        sliderSizer.Add(self.trackCounter, 0, wx.ALL|wx.CENTER, 5)
        
        # set up playback timer
        self.playbackTimer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.on_update_playback)
                
        mainSizer.Add(self.mplayer, 1, wx.ALL|wx.EXPAND, 5)
        mainSizer.Add(sliderSizer, 0, wx.ALL|wx.EXPAND, 5)
        mainSizer.Add(controlSizer, 0, wx.ALL|wx.CENTER, 5)
        self.panel.SetSizer(mainSizer)
        
        self.Bind(mpc.EVT_MEDIA_STARTED, self.on_media_started)
        self.Bind(mpc.EVT_MEDIA_FINISHED, self.on_media_finished)
        self.Bind(mpc.EVT_PROCESS_STARTED, self.on_process_started)
        self.Bind(mpc.EVT_PROCESS_STOPPED, self.on_process_stopped)
        
        self.Show()
        self.panel.Layout()
        
    #----------------------------------------------------------------------
    def build_btn(self, btnDict, sizer):
        """"""
        bmp = btnDict['bitmap']
        handler = btnDict['handler']
                
        img = wx.Bitmap(os.path.join(bitmapDir, bmp))
        btn = buttons.GenBitmapButton(self.panel, bitmap=img,
                                      name=btnDict['name'])
        btn.SetInitialSize()
        btn.Bind(wx.EVT_BUTTON, handler)
        sizer.Add(btn, 0, wx.LEFT, 3)
        
    #----------------------------------------------------------------------
    def build_controls(self):
        """
        Builds the audio bar controls
        """
        controlSizer = wx.BoxSizer(wx.HORIZONTAL)
        
        btnData = [{'bitmap':'player_pause.png', 
                    'handler':self.on_pause, 'name':'pause'},
                   {'bitmap':'player_stop.png',
                    'handler':self.on_stop, 'name':'stop'}]
        for btn in btnData:
            self.build_btn(btn, controlSizer)
            
        return controlSizer
    
    #----------------------------------------------------------------------
    def create_menu(self):
        """
        Creates a menu
        """
        menubar = wx.MenuBar()
        fileMenu = wx.Menu()
        add_file_menu_item = fileMenu.Append(wx.NewId(), "&Add File", "Add Media File")
        menubar.Append(fileMenu, '&File')
        
        self.SetMenuBar(menubar)
        self.Bind(wx.EVT_MENU, self.on_add_file, add_file_menu_item)
        
    #----------------------------------------------------------------------
    def on_add_file(self, event):
        """
        Add a Movie and start playing it
        """
        wildcard = "Media Files (*.*)|*.*"
        dlg = wx.FileDialog(
            self, message="Choose a file",
            defaultDir=self.currentFolder, 
            defaultFile="",
            wildcard=wildcard,
            style=wx.OPEN | wx.CHANGE_DIR
            )
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            self.currentFolder = os.path.dirname(path[0])
            trackPath = '"%s"' % path.replace("\\", "/")
            self.mplayer.Loadfile(trackPath)
            
            t_len = self.mplayer.GetTimeLength()
            self.playbackSlider.SetRange(0, t_len)
            self.playbackTimer.Start(100)
            
        
    #----------------------------------------------------------------------
    def on_media_started(self, event):
        print 'Media started!'
        
    #----------------------------------------------------------------------
    def on_media_finished(self, event):
        print 'Media finished!'
        self.playbackTimer.Stop()
        
    #----------------------------------------------------------------------
    def on_pause(self, event):
        """"""
        if self.playbackTimer.IsRunning():
            print "pausing..."
            self.mplayer.Pause()
            self.playbackTimer.Stop()
        else:
            print "unpausing..."
            self.mplayer.Pause()
            self.playbackTimer.Start()
        
    #----------------------------------------------------------------------
    def on_process_started(self, event):
        print 'Process started!'
        
    #----------------------------------------------------------------------
    def on_process_stopped(self, event):
        print 'Process stopped!'
        
    #----------------------------------------------------------------------
    def on_set_volume(self, event):
        """
        Sets the volume of the music player
        """
        self.currentVolume = self.volumeCtrl.GetValue()
        self.mplayer.SetProperty("volume", self.currentVolume)
        
    #----------------------------------------------------------------------
    def on_stop(self, event):
        """"""
        print "stopping..."
        self.mplayer.Stop()
        self.playbackTimer.Stop()
        
    #----------------------------------------------------------------------
    def on_update_playback(self, event):
        """
        Updates playback slider and track counter
        """
        try:
            offset = self.mplayer.GetTimePos()
        except:
            return
        print offset
        mod_off = str(offset)[-1]
        if mod_off == '0':
            print "mod_off"
            offset = int(offset)
            self.playbackSlider.SetValue(offset)
            secsPlayed = time.strftime('%M:%S', time.gmtime(offset))
            self.trackCounter.SetLabel(secsPlayed)  

We’re going to focus on the hightlights and ignore the boilerplate. If you don’t know wxPython code, then go to the wxPython website and read the tutorials. Now, to create an instance of the MplayerCtrl, we need to call mpc.MplayerCtrl(self.panel, -1, mplayer) and pass in the path to the mplayer. To keep track of how much of the movie has played, we create a wx.Slider. We also use a slider for the volume control. We use some of wx’s generic button widgets to give us some easy-to-use bitmap buttons. Finally, we create a timer that we’ll use to update the playback slider’s position. To load a movie and start it playing, we create a menu item.

The build_btn, build_controls, and create_menu are helper methods that build our GUI. You can figure those out for yourself. We’ll just look at a few of the mplayer-related methods. In on_add_file, we open a FileDialog and let the user find a movie file on their PC. Once they’ve picked one out, we hack the path to the file slightly to make it more palatable to mplayer by replacing backslashes with forward slashes. Then we call our MPlayerCtrl’s LoadFile method to load the movie into mplayer and get it started. We also start our timer and update the playback slider.

You can Pause or Stop the playback with the two buttons that call the appropriate methods. The MplayerCtrl also exposes lots of other methods that we can use to get the length of the movie, get the bitrate, the codec, and more. Check out the documentation to see what all you can accomplish.

Wrapping Up

As you can see, creating a simple media player in Python is really easy. Getting the MplayerCtrl to play something takes about 5 minutes. Adding the sliders and functions add 30-45 minutes (depending on your skillz). Feel free to add enhancements, like a playlist, a way to speed up or slow down the playback or get the frame to resize the movie so its aspect ratio is constant. Note that sometimes mplayer doesn’t want to kill the movie when you close the frame. This is a bug which the author of the control is trying to figure out. Anyway, have fun watching movies with Python!

Note: This code was tested with MplayerCtrl 0.1.2 and 0.2.4, wxPython 2.8.10.1, Python 2.5/2.6 on Windows XP and Windows 7. I used the mplayer rtm-svn-31170 version in my testing as well.

Further Information

Downloads

3 thoughts on “wxPython: Creating a Simple Media Player”

  1. Thx for this great blogpost :), I'll add a link at the MplayerCtrl docs.
    Btw. there's nothing left to add.

    – Dav1d

  2. Hi Aigars,

    I've heard that GStreamer is getting pretty popular. I just checked their website to see how its cross-platform support is and GStreamer doesn't appear to have any Mac builds and no official Windows builds. On the other hand, mplayer does support all three major platforms. I may take a look at GStreamer anyway just for fun though.

    Thanks for the feedback!

    – Mike

Comments are closed.