How to make a GUI with Tkinter to open mp4 videos in a folder

Let’s design a very simple app to open mp4 files in a folder with python and then launch the movies.

Let’s say we have a folder with this files inside:

The folder with the files I want to use in the python code
The folder with the files I want to use in the python code

We will make an app that look into this folder and get al list of all the mp4 with

glob("*.mp4")

Let’s see this in action in cmd:

An example of use and output of the very useful function glob

Now we want to put all the mp4 names in a listbox, a tkinter listbox.

How to populate a list with the folder’s file

We need:

  • glob
  • tkinter
  • tkinter.Listbox
  • a for loop

Let’s do it:

from glob import glob
import tkinter as tk

root = tk.Tk()
lb = tk.Listbox(root)
lb.pack()
fl = glob("H:\\ffmpeg\\*.mp4")
for f in fl:
    lb.insert(0,f)

root.mainloop()

Output:

Window with list of mp4 files in the folder H:\\ffmpeg

We want to open the file that we double click when we select it.

This is how you can bind and action on a widget:

  • write the name of the widget (lb)
  • follow with a dot
  • write bind
  • in round brackets write (“<Double-Button>”, lambda x: play())
lb.bind("<<Double-Button>>", lambda x: play())

And then write the function play, where you will start the mp4 file selected.

You need:

  • the number of item selected from x = lb.curselection()
  • the name of the file with fname = lb.get(x)
    • or in one line, if you prefer:
      • fname = lb.get(lb.curselection())
  • then you start this file with the startfile function of the os module, right?

This is it in practise (remember that we have to add import os at the beginning):

def play():
	fname = lb.get(lb.curselection())
	os.startfile(fname)
# put the bind after the function play
lb.bind("<Double-Button>", lambda x: play())

Now when you double click on an mp4 file in the window with the list, the file will be opened by the operating system default video player software, it will change on different computers or you will be invited to choose a software on your pc to perform this action on this type of files (mp4) if you haven’t done it yet.

This is the very basic thing we can do. Let’s add som features now.

Properties of the mp4 files… in the GUI: size!

We want to add something that is useful to this app. When we select a file, some properties will appear at the bottom of the window:

  • file size in megabytes
  • duration in seconds

Get the size

To get the size of a file is quite easy.

  • use os.path.getsize(filename)

We have the filename, right? We have:

  • fname = lb.get(lb.curselection())

Let’s make this a global variable to avoid having to repeat the same code twice.

The property will be shown when the item in the listbos is selected and we will show it into a label that we are going to create.

So we need:

  • to create a function see_properties() to perform the action (os.path.getsize …)
  • to bind the selection on the listbox item to this action with “<<ListboxSelect>>” (notice the double <<>>)
  • a label to read the properties

Let’s go step by step, then.

Create label (well it was the 3rd step… but… it’s better to start here)

This is where the user will know the size of the selected item (the video size):

la = tk.Label(root, text = "Select a file")
la.pack()

Function see_properties and … fname

To make life simplier we create a functon to get the name of the file selected (because we will use this more times):

def fname():
	return lb.get(lb.curselection())

To see the properties now we get the name with fname() and we put in os.path.getsize( fname() )

def see_properties():
	size = os.path.getsize(fname())
	la['text'] = size

So… also the function play now is simplified:

def play():
	os.startfile(fname())

It’s all very simple, we take the name, we store it’s size, write it in the label… but when this will be done? When you will select the video file name. The computer knows yet that he must link the selection of the item in the listbox to this function to show the size in the label? No, computers waits always for a precise command, they do nothing if you do not tell them to do it (unless you are doing machine learning). You have to tell the computer to link the selection to the function with the bind method, in a very similar way to what we already did to open the movie. The difference here is that the action is a selection (not a double click) and the action is contained in the see_properties function (and not in the play function like we did before). So, this is the code:

lb.bind("<<ListboxSelect>>", lambda x: see_properties())

The most difficult thing here is to remember the words of the name of the event. This time is “<<ListboxSelect>>” and it hase double angular brackets, while the “<Double-Button>” has only one angular brackets (he is Double but has one brackets, the other has double angular brackets… there must be a reason for this… If someone know it…). Ok, I told you this just to make you remember this (but I always forget it :)).

The code until now

from glob import glob
import tkinter as tk
import os


root = tk.Tk()
lb = tk.Listbox(root)
lb.pack()
fl = glob("H:\\ffmpeg\\*.mp4")
for f in fl:
    lb.insert(0,f)

def fname():
	return lb.get(lb.curselection())

def play():
	os.startfile(fname())

lb.bind("<Double-Button>", lambda x: play())

la = tk.Label(root, text = "Select a file")
la.pack()

def see_properties():
	size = os.path.getsize(fname())
	la['text'] = size

lb.bind("<<ListboxSelect>>", lambda x: see_properties())

root.mainloop()
The numer of size in bytes of the file selected in not very readable

As you can see, in the label below there is the size in bytes. We may need to improve these numbers and make the more readable. We will simply divide it by 1.000.000 (of 1,000,000 for who uses , instead of . to divide thousands).

def see_properties():
	size = os.path.getsize(fname()) // 1000000
	la['text'] = f"Size: {size} Mb"

We also added some nice formatting with f”{size} Mb”. Python lately let you do this, so that in a string (with the f before the ” you can put variables into the curly brakets. It is very neat.

Now the same window looks like this:

Now we have another idea… What about duration of the file… this is something that could interest someone to know when he checks for the videos.

There are many solutions for this, but I found this one that is very simple:

  • install pymediainfo
  • from this module import MediaInfo
  • use its method parse to the file name (we will get it with fname())
  • then we will get the duration in seconds with this strange formula:
    • seconds = int(media_info.tracks[0].duration / 3600) *4
  • Now we are ready to show this result in the label “la”
    • filedata = f”File size: {size:n} MB\n Duration: {seconds} seconds”
    • la[‘text’] = filedata

The function see_properties becomes:

def see_properties():
	size = os.path.getsize(fname()) // 1000000
	duration = MediaInfo.parse(fname())
	ms = int(duration.tracks[0].duration / 3600) *4
	filedata = f"File size: {size:n} MB\n Duration: {ms} seconds"
	la['text'] = filedata

The whole code

Now let’s see the result. We should get minutes when duration i more than 60 seconds.

from glob import glob
import tkinter as tk
import os
from pymediainfo import MediaInfo

root = tk.Tk()
lb = tk.Listbox(root)
lb.pack()
fl = glob("H:\\ffmpeg\\*.mp4")
for f in fl:
    lb.insert(0,f)

def fname():
	return lb.get(lb.curselection())

def play():
	os.startfile(fname())

lb.bind("<Double-Button>", lambda x: play())

la = tk.Label(root, text = "Select a file")
la.pack()

def see_properties():
	size = os.path.getsize(fname()) // 1000000
	duration = MediaInfo.parse(fname())
	ms = int(duration.tracks[0].duration / 3600) *4
	filedata = f"File size: {size:n} MB\n Duration: {ms} seconds"
	la['text'] = filedata

lb.bind("<<ListboxSelect>>", lambda x: see_properties())



root.mainloop()

This is the output:

To be continued…

Tkinter test for students

Tkinter articles

Published by pythonprogramming

Started with basic on the spectrum, loved javascript in the 90ies and python in the 2000, now I am back with python, still making some javascript stuff when needed.