/* jquery */ /* jquery accordion style*/ /* jquery init */

28 April 2013

Learn Python - GUI File Search App

Now we've seen how a typical GUI program is structured, let's try a more advanced example.

It will be based on the File Walk App we created earlier, which searched the file system, using the 'os'walk' function to obtain a list of files and sub-directories.

Open another new file in Geany, save it as 'tk-search.py' and type in the code below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# A GUI search program using tkinter
# Created by David Briddock

from tkinter import *
import os

# initialise main window
def init(win):
  win.title("File Search")

  labelPath.grid(row=0, column=0, sticky="W")
  entryPath.grid(row=1, column=0)
  labelEnding.grid(row=2, column=0, sticky="W")
  entryEnding.grid(row=3, column=0)
  btnSearch.grid(row=4, column=0)
  fileList.grid(row=0, column=1, rowspan=5)
  yscroll.grid(row=0, column=2, rowspan=5, sticky="NS")

  fileList.configure(yscrollcommand = yscroll.set)
  yscroll.configure(command = fileList.yview)
  entryPath.insert(INSERT, "/home")
  entryEnding.insert(INSERT, ".py")

# find button callback
def search():
  # get start directory and file ending
  startDir = entryPath.get()
  fileEnding = entryEnding.get()

  # clear the listbox
  fileList.delete(0, END)

  # find matching file and fill listbox
  for path, dirs, files in os.walk(startDir):
    for fileName in files:
      if (fileName.endswith(fileEnding)):
        fileList.insert(END, path+"/"+fileName)

# create top-level window object
win = Tk()

# create widgets
labelPath = Label(win, text="Starting Path")
entryPath = Entry(win, width=12)
labelEnding = Label(win, text="File Ending")
entryEnding = Entry(win, width=12)
fileList = Listbox(win, width=80)
yscroll = Scrollbar(win, orient=VERTICAL)
btnSearch = Button(win, text="Search", width=8, command=search)

# initialise and run main loop
init(win)
mainloop()

While there are quite a few lines of code here, I hope you can pick out the function definitions and typical GUI app structure.

We've still have the 'Tkinter' module import on line 4, while line 5 imports the 'os' module. You'll also notice the 'init' function and another event callback function, this time called 'search'. More on these later.

More Widgets

On line 40 we create the top-level window object. This time we need quite a few widgets. These are created on lines 42 to 49.

The labels and entry boxes will provide our user input interface. The label text is set using the 'text' configuration parameter. The two entry fields will hold the starting directory and file ending strings, and have a display width assignment of twelve characters.

On line 47 a listbox widget is created. This will hold a list of matched files. Specifying a width of 80 characters will cater for any long path names. A search might find many files. So, we need a vertically oriented scrollbar. We'll 'attach' this scrollbar to the listbox later.

And finally, on line 49, we create a button widget to kick off the search operation. The three parameters specify the button label text, the callback function name, and a width of eight characters.

Lines 52 and 53 call the 'init' function and start the main window loop, just like we saw in the Basic GUI App post.

Using Grid Layout

Now back to the 'init' function. With more widgets it's quite a bit longer than before, and there's also something new to learn. Our widgets need to be positioned in particular locations. Luckily, the Tkinter module has a built-in grid layout feature.

You can think of a grid layout as a collection of empty boxes or cells, organised into rows and columns - very like the cells in a spreadsheet. The cells are numbered from the top left corner starting from zero. A widget is placed in a particular grid cell with the widget's 'grid' function.

We start on line 11 with the cell at column zero row zero, which will contain the starting directory label widget. On line 12 we populate the cell in column zero row one with the starting directory entry widget. Lines 13 and 14 repeat this process for the file ending's label and entry widgets for rows three and four. On line 15 we add the search button to row five in column zero.

By now I'm sure you noticed the 'sticky' configuration parameter on the labels. The 'W' setting means 'West' and ensures the label text will line up on the left hand side of the column.

The second column, column one, only contains the listbox widget. However, the 'rowspan' configuration parameter will stretch it to fill five rows - matching the number or rows we assigned in column zero.

The final column has the scrollbar. This is also stretched over five rows and has a 'NorthSouth' stickiness to ensure it fills the full column space. We 'attach' the scrollbar to the listbox using the statements on lines 19 and 20. This will ensure the listbox contents scroll when the scrollbar is moved.

The last two lines of the function, 21 and 22, set default text string values for the entry widgets.

The Search Callback

Now for the 'search' function. Remember this is called when the search button is clicked, as per the callback setting on line 49. The first two lines, 27 and 28, extract the contents of the entry widgets and store them in two string variables, 'startDir' and 'fileEnding'.

Line 31 deletes all the items in the listbox, to remove any files from a previous search.

Our search is done in lines 34 to 37. Line 34 starts a loop and calls the 'os.walk' function with our 'startDir' string. This function returns a list of paths, files and sub-directories. Line 35 is another loop which extracts each file in the file list, and stores it in the 'fileName' variable.

The conditional 'if' statement on line 36 compares the end of the 'fileName' string with our entered 'fileEnding'. If the test is 'True' then it adds a new string to the listbox, using the path and file name.

And that's it. Run the program using Geany's 'Build->Execute' menu option, or F5 key.

Depending on the type and version of your Raspberry Pi image, you may see a message that says 'No module named Tkinter'. In this case see my previous Installing Tkinter post.

Experiment by trying different file endings and starting directories.

Make It Your Own

As we've seen a GUI program requires quite a bit more code than a simple terminal-based one. However, the flexibility offered by a widget-based user interface is certainly worth the effort.

GUI programming is a big subject. In this short article we've barely scratched the surface. The possibilities for refinement are almost endless. For example, you could change a widget's size or colour, add new widgets, or create a completely different layout.

Python 2.7

Remember, for Python 2.7 you'll need the Tkinter. For more information see my previous Installing Tkinter post.

A post from my Learn Python on the Raspberry Pi tutorial.