Python & CGI Programming


Tkinter Status Bar

This is not CGI programming, but a Python Tkinter coding problem that took me six months to solve. I wrote this page so hopefully the next Tkinter coder that has this problem won't waste six months.

I developed a process that gathers statistics about the results of Go games from the KGS Go Server. If you're wondering what the game of Go is, you can click here for more information.

Here is an image of the KGS Statistics screen

Screen image of KGS Statistics

The most time-consuming process is the lookup process (the first button on the screen). Go game statistics for a person are retrieved from web pages created by the KGS server. The KGS server creates one web page for each month of Go games a person has played. After each page is retrieved, it is processed to extract the game record information. It takes about five seconds to retrieve and process one web page. The process was slowed down so the program does not overload the KGS server.

I wanted a status bar (seen in the image as a red bar) to show the status of the web page retrieval and processing. For a person that has several years of Go game records, this process can take a minute or two to complete.

The problem that took six months to solve was that I couldn't "pause" the process and redisplay the status bar to show the progress of the lookup process. Every example of a Python Tkinter status bar I could find on the web used the .after handler to "pause" processing and redisplay the screen.

The .after handler is great for simple processes. Since the .after handler not only has to call the function, but also be the last statement of the function, the function has to be simple. I could not figure out how to use the .after handler in my KGS lookup processing. Finally, I discovered the .update event processing handler would do what I needed.

Here is the function from KGS Statistics that retrieves one web page, processes it, draws the status bar, and calls update to redraw the screen to show the status of the lookup process.


def ExtractPriorMonth(month, total):   
       
    try:
        Page          = urllib.urlopen(URL + month)
        html          = Page.read()
        games_table   = ExtractGames(html)
        BarCanvas.coords(RectangleID, 0, 0, 
                         int(total / PROGRESS_BAR_RES), PROGRESS_BAR_HEIGHT)
        RootWindow.update()
        
    except IOError:
        games_table   = "Extraction of games from KGS server timed out"

    return games_table

As you can see in the code, the first line opens the web page, and the second line reads the html from the page. The ExtractGames process extracts the game information from the web page and saves it in the games_table tuple, one tuple for each game result. The BarCanvas is the status bar widget. The total is the length of the bar so far and is calculated elsewhere and passed to this function. The RootWindow is the instance of the root window.

The .update handler redraws the screen, showing the status bar while the lookup process is executing.

Here is the function that calls ExtractPriorMonth


def GetGamesTable(html):
	
	# extract the HTML for all of the months for the player

	months_table = ExtractMonths(html)
	count        = len(months_table)
	increment    = int(PROGRESS_BAR_LENGTH / count)
	total        = 0
	
	# extract the games from the most recent month

	games_table  = ExtractGames(html) 

	# extract the games from all of the prior months

	for month in months_table:
		month          = month.replace('&', '&')
		total         += increment
		games_month    = ExtractPriorMonth(month, total)
		
		if (type(games_month) == types.StringType):
			games_table  = games_month
			break
		else:
			games_table += games_month

	return games_table

The first web page retrieved has a list of all the prior month web pages, as well as the most recent month of game statistics. The most interesting thing in this function, from the standpoint of the status bar, is the calculation of increment and total for the ExtractPriorMonth function. You can see how the ExtractPriorMonth function is called as many times as there are months of game statistics.

I hope this was a helpful illustration of a functional status bar in a Tkinter screen using the .update handler.

Top Page: Index