This is the last article in a series, to read the entire series go here.

Writing a GUI for our RSS Aggregator

The Plan

I'll be finishing up the Python set with an overview of the Tkinter GUI library. There are numerous other GUI libraries available for Python, the best known of which is wxPython. I chose Tkinter because it comes built into Python and the primary goal here is to, as much as possible, look at Python as it comes out of the box.

What to Expect

When this article is complete:

You will have:

A basic GUI for last week's RSS reader.

You will know:

How to write a GUI in Python's Tkinter.

How to use lambda expressions to create functions on the fly.

The final GUI will look like this:

Files Used in this Project

RssReader.txt: A library for reading RSS information, that I wrote in the previous project. Rename it to "RssReader.py" after downloading.

RssReaderGui.txt: The sourcecode for this project. Rename it to "RssReaderGui.py" after downloading.

The Code

Libraries

Global Variables

currentFeeds = {}

rssDisplay = None

chooseChannel = None

currentChannel = None

currentFeeds = None

I've seen two ways to organizing the code for a simple one window GUI in Python. One is to wrap it all in a single large class. The other is to write a group of functions that share a set of global variables. In this case a single class would have the same problems as globals along with the overhead of adding self. to every method call and class variable.

The variables that are set to None above are place holders for global variables that will be assigned later. They don't have to be assigned here, but it's useful to know what your globals are at the front end.

Toplevel Window Creation

def rssWindow():

root = Tk()

root.title("Rss Reader")

root.geometry("750x500")

root = channelFrame(root)

root = buttonFrame(root)

return root

Although you can define your functions in arbitrary order, I've written these in a, more or less, heirarchical order. I'll walk through what I did here line by line:

At the top level we create a Tk object.

Give the window a title

and an initial size.

The frame that contains the drop down menu and list of articles.

The frame that contains the select and load buttons.

The Upper Window with the Dropdown and Listbox Layout

def channelFrame(parent):

channelFrame = Frame(parent)

channelSelectFrame = Frame(channelFrame)

channelSelectFrame = channelLabel(channelSelectFrame)

channelSelectFrame = channelSelect(channelSelectFrame)

channelSelectFrame.pack(side=LEFT)

channelFrame = rssDisplay(channelFrame)

channelFrame.pack(side=TOP, expand=YES, fill=BOTH)

return parent

Here we create the window that contains textual information from the RSS feeds. To accomplish this, I create frames which contain either other frames or GUI elements. Once the elements are created and organized by frame they are in, then the window behavior is set with .pack

def rssDisplay(parent):

global rssDisplay

rssDisplay = Listbox(parent)

rssDisplay.pack(side=RIGHT, expand=YES, fill=BOTH)

return parent

def channelLabel(parent):

label = Label(parent, text="Select a channel:")

label.pack(side=TOP)

return parent

def channelSelect(parent):

global chooseChannel, currentChannel

currentChannel = StringVar(parent)

channelList = ["None"]

currentChannel.set(channelList[0])

chooseChannel = OptionMenu(parent, currentChannel, *channelList)

chooseChannel.pack(side=BOTTOM)

return parent

Here the individual components are generated, the behaviors of each are described later. On line 2 I used the global keyword. You only need to use this keyword if you plan on changing the global variable, not if you're going to access it.

The Lower Window with the 'Set Config File' and 'Load Feeds' Buttons

def buttonFrame(parent):

buttonFrame = Frame(parent)

buttonFrame = setConfigFileButton(buttonFrame)

buttonFrame = loadRssButton(buttonFrame)

buttonFrame.pack(side=RIGHT, expand=YES, fill=X)

return parent

def setConfigFileButton(parent):

button = Button(parent, command=setConfigFile)

button["text"] = "Set Config File"

button.pack(side=LEFT)

return parent

def loadRssButton(parent):

button = Button(parent, command=loadRss)

button["text"] = "Load Feeds"

button.pack(side=RIGHT)

return parent

Here I create a top level layout for each of the buttons then describe the specific behavior of each one. We describe the buttons behavior by passing the function to be called via the command argument.

Commands called by various elements

Lambda, functions are first class objects

def loadRss():

global chooseChannel, currentChannel

chooseChannel["menu"].delete(0, END)

channelList = currentFeeds.keys()

for channelName in channelList:

chooseChannel["menu"].add_command(label=channelName,

command=lambda(temp = channelName): selectChannel(temp))

selectChannel(channelList[0])

The loadRss function clears the values from the drop down menu then adds each of the current channels to it. It also uses a Lambda function to set the behavior of the drop down box when a given channel is selected.

Lambda functions, as used in line 7 of the above code are extraordinarily useful. It comes in handy, as in the above case, when you need to create a function for which some of the internal values are not known until runtime. Here the Lambda function's purpose is to assign the default value, channelName to the selectChannel function and assigns it as chooseChannel's command.

In general Lambda functions come in the form lambda arg, arg: arg + arg. For people who aren't used to functional it's important to remember that there's only ever one line in a lambda function and the value of that line is always returned without any need to use the return keyword.

When one of the channels is selected from the drop down, this function fires and populates the display window with the names of the current articles.

def setConfigFile():

global currentFeeds

currentFeedFile = askopenfilename(filetypes=[("allfiles", "*"),

("textfiles","*.txt")])

currentFeeds = combineFeeds(currentFeedFile)

loadRss()

Here the program opens a file dialog with askopenfilename which will return the flie selected. It then combines all of the feeds with the same channel name and loads them into the select box to display them with loadRss.

def combineFeeds(fileName):

feeds = {}

for feed in generateRsses(fileName):

for channelName in feed.keys():

if feeds.has_key(channelName):

feeds[channelName] += feed[channelName]

else:

feeds[channelName] = feed[channelName]

return feeds

combineFeeds reads the RSS sources from the given filename, downloads the feeds using the library from the last article then combines any channels that happen to have the same name.

if __name__ == "__main__":

rssWindow().mainloop()

To actually create the window and make it useful, the program calls rssWindow(), described at top of the file, then runs the mainloop() method on it.

This has been an interesting run, next week I'll be taking a look at Markdown, a lightweight markup language which allows you to create nicely typset documents from the comfort of your text editor. Starting in December I'll be taking a look through Scala a functional/object oriented language that runs in the Java VM.

Writing GUI Code in Scala

This is the last article in a series, to read the whole series go here.

The Plan

For the last week of Scala I've put together a GUI for week 3's RSS aggregator using the Swing library. Scala's Swing library is a fairly thin wrapper around Java's Swing library. The primary difference between them is that Scala's library takes advantage of its more powerful type system.

What to Expect

When this article is complete:

You will have:

A front end for the RSS agregator I wrote in week 3.

You will know:

How to write a basic Swing application in Scala.

Screenshot:

Files Used in this Project

RssReader.scala: A library for reading RSS information, that I wrote in the previous project.

Container for RSS Data

This will hold the data for the RSS feeds. I've chosen to initialize it with "None Loaded" and an empty string because it gives the GUI something to display until a config file is selected and loaded, and it let's Scala know what the rssFeeds's type is.

File Chooser

The configFileChooser is a FileChooser that will open, defaulting to the current working directory. configFileName will hold the value selected, the details for how this will work are in the "Defining Behaviors" section.

Layout

For the most part the code here stands for itself, which is wonderful. Once you understand the basics for creation and layout for one component, they're all the same. As a result I've be described each new idea in detail when I encountered it and let the rest alone.

Lines 1-3: Create a new label with "Select a channel:" for its value.
Line 5: Create a ComboBox that is initialized to the first tuple value in the first list value: "None Loaded"
Line 7: Create a new panel that is filled top to bottom.
Line 8-9: Add the Label and ComboBox to the panel.
Line 10: Surround the panel in an empty border with 30 pixels on the right side (to separate it from the RssDisplay).

A New Channel is Selected

Line 1: The case looks for the event, SelectionChanged and to which element that event is applied, channelSelect, the backquotes above are important.
Line 2: When the selection changes, set the display to contain the story titles from the rssFeeds value with the same index as the value selected.
Line 3: Once the values have been changed, packs the window so everything fits.

Line 2: Verifies that some value has been assigned to configFileName.
Line 3: Uses combineFeeds from RssReader to read in the RSS values.
Line 4: Set's the channelSelect ComboBox to contain the names of each of the RSS feeds.
Line 5. Set's the rssDisplay to contain the titles from teh first feed.

Final Summary

Scala has been an extraordinarily interesting language. The XML package alone makes it worth adding to your toolbox and, as can be seen by how clean the GUI design code is, it's not a one trick pony. The type system, which sadly I haven't been able to go into nearly enough detail here, has many of the nice features you'll find in Haskell. And of course it's great to be able to make use of all of Java's libraries for free.

I do have a couple of small complaints:

The error messages are sometimes a bit obscure, but this is a common flaw in all but the most mature open source projects.

When code compiles, each anonymous function gets its own class. For anyone coding in a functional style, this quickly leads to a deeply cluttered directory.

These complaints are minor though, and the first can certainly be fixed. Overall it's been a pleasure to work with and I look forward to using it for real tasks in the near future.

Coming Up

Next week I'll be starting on Prolog. Prolog is a declarative language that is centered on formal logic.

The Code

Compiling the Code... More or Less

I discovered that compiling GUI code for XPCE is easier said than done. I made several attempts, read a couple of tutorials and got nowhere. If there are any Prolog guru's reading who wouldd like to set me straight, please leave a comment.

On the other hand, running the code on the is pretty easy:

Download the files for this project and re-name them as described above.

XPCE makes it possible to access objects in Prolog by adding three predicates:

new: Instantiates a new object.

send: Sends information to the object.

get: Gets information from an object.

To make a new GUI window, first a frame is created(Line 2) which contains all the GUI elements.
Lines 3 and 4 create a List Box to contain the channel names and adds it to the Frame.
Line 5 adds a List Box to the right side of the channel list box for displaying news titles.
Line 6 adds an action so that when a value is selected in RssFeeds, the newsDisplay predicate is fired with RssFeeds and NewsList for arguments.
Line 7 adds a container for a button.
Line 8 adds a button to the dialog which has the text "load" and fires load when clicked.
Line 9 opens the frame.

Read the Config and Load the RSS Feeds

load(Browser):-

get(@finder, file, exists :=@on, FileName),

readConfig(FileName, URLs),

send(Browser, clear),

foreach(member(URL, URLs), readAndListChannels(Browser, URL)).

Line 2 opens up a standard file dialog and unifies the selected file with FileName, from there the code is much like the display code from last week.

I used a separate predicate to write each of the URLs to avoid potential logical contradictions in the foreach.

Again, much of this is similar to the code to display the feeds on the command line. The only real change is in Line 5, where the data from each channel is wrapped into a dictionary item with:

identifier: Name

key value: Name

object: News

As before, this will keep trying until it succeeds or blows the stack.

Display the News Titles in the NewsList Browser

newsDisplay(RssFeeds, NewsList):-

get(RssFeeds, selection, Channel),

get(Channel, object, News),

get(News, size, SizeVal),

send(NewsList, clear),

foreach(between(1, SizeVal, LineNumber),

sendVector(NewsList, News, LineNumber)).

Line 2 unifies Channel with the currently selected item in RssFeeds, the dictionary item from readAndListChannels.
Line 3 pulls the object from the dictionary item, a vector.
Line 4 determines the length of the vector.
Line 5 clears the NewsList so we don't just keep adding to the end.
Line 6-7 iterates over each of the values in the vector and appends them to NewsList with sendVector.

Append a Cell from a Vector to a Browser

sendVector(Browser, Vector, Index):-

get(Vector, element, Index, Value),

send(Browser, append, Value).

Given a Browser, Vector and Index, sendVector pulls the Indexth element from the Vector and appends it to the Browser.

Final Summary

Prolog's logic based semantics make for an interesting programming experience. Up to this point I had trouble imagining how you'd deal with dynamic situations, like user input or a file being read in, in context to a language that is based on logic. Now that I've spent a month working with it, it's an incredibly elegant way to solve problems. That being said, I think I'm going to stick with Python for my day to day scripting needs.

Coming Up

Next week is a fifth Monday, so I'll be working with a tool, Robot Framework. Robot Framework is a Python based glue language for writing test cases in a clear format which is readable to non-technical users.

In February, I'll be working with Squeak. Squeak several neat features, it is:

generally considered the first full object oriented language

where unit tests frameworks came from (in the form that most of us are used to today)