So far our RSS reader has been completely command-line, which is functional but not as nice as we’d really like to have it. So what we are going to do is integrate the GUI that I created in my two Tkinter GUI tutorials into our RSS application.
If you look at our GUI application you can see that at this point it actually has the basic look that we’ll want, what it doesn’t have is the functionality attached to to GUI. So the first thing that we are going to do is let the user add an RSS site when the press the Add button. We are going to pop up a dialog and let them enter the name of the RSS site and the address of the RSS feed.
In order to do that we are going to have to create a simple dialog. Fortunately Tkinter has a nice simple dialog interface that makes creating simple pop-up dialogs quite easy. It’s not as robust as you might like, but if you really wanted to do something very complicated you could simply create another window in the same way that we created our main window. Since we don’t need anything fancy we”ll use the Simple dialog.
To start we must import the simple dialog into our program, so the following will have to be added to the top of the code:
Now we could create create our dialog class in a totally new python file but since it’s going to be pretty simple I decided to keep all together in our one GUI file. The first step in understanding the simple dialog is to read PythonWare’s introduction to the dialog window it will explain all that you need to know to create a simple dialog.
Basically what you need to do is create a class whose parent is the simple dialog:
Then you have to create a body function, this is the function that the tkSimpleDialog assumes will be used to create the body of the dialog. The definition of the body function is as follows:
def body(self, master):
Pretty simple so far right? There are a few other important functions that you need to know about to create a simple dialog, like when to process the data after the Ok button is pressed:
When the cancel or Ok button is pressed:
def cancel(self, event): def ok(self, event):
You can read this documentation on sourceforge is you need to know more. For our simple dialog we only care about the body and apply functions. The only other feature that I wanted to add to this dialog was a simple way to set the default values of the RSS name and RSS Feed URL.
That way it would be easy to use this dialog to add and edit RSS sites. To do so you have add and __init__ function to the class, it is defined as:
def __init__(self, parent, title=None, RSSName="", RSSUrl=""):
Where self, parent, and title are the parameters expected by tkSimpleDialog, title being the dialogs title, and RSSName and RSSUrl our new parameters. So here is the RSSDialog’s code in it’s entirety, if you are familiar with tkInter at all it should be pretty simple, if you have any questions ask them and I will try to answer them:
class RSSItemDialog(tkSimpleDialog.Dialog): def __init__(self, parent, title=None, RSSName="", RSSUrl=""): """Init, override default for default params""" self.RSSName = RSSName self.RSSUrl = RSSUrl self.Result = False #Default = Cancel tkSimpleDialog.Dialog.__init__(self, parent,title) def body(self, master): """This is where you create the Body of the dlg""" lbText = Label(master, text="Name") lbText.grid(row=0, padx=5, pady=10,sticky=W) lbText = Label(master, text="URL:") lbText.grid(row=1, padx=5,sticky=W) self.eRSSName = Entry(master) self.eRSSName.insert(0,self.RSSName) self.eRSSUrl = Entry(master) self.eRSSUrl.insert(0,self.RSSUrl) self.eRSSName.grid(row=0, column=1) self.eRSSUrl.grid(row=1, column=1) """Return the widget that will get focus""" return self.eRSSName def apply(self): """They have selected OK""" self.Result = True self.RSSName = (self.eRSSName.get()) self.RSSUrl = (self.eRSSUrl.get())
Now we need to do a few things, we need to save a list of RSS sites in our GUIFramework class, this will correspond to the list of items in the Listbox. To do so we add the following code to the __init__ function:
self.lstSites = 
This is a list of RSS sites that the user has added to our application. We will also need a new class to contain all of the RSSSite information, the class is pretty simple:
class RSSSite: """An RSS Site""" def __init__(self, Name="", Url=""): self.Name = Name self.Url = Url
Then we have to create a function that will correspond to when the Add button is pressed. This dialog will show the RSSItemDialog and then if Ok is press add the resulting RSSSite to the RSS Site Listbox (lbSites) and store the site in the lstSites list. The code to accomplish this is as follows:
def AddRSSItem(self): """Add an RSSItem""" RSSDlg = RSSItemDialog(self.master,"Add new Item") if (RSSDlg.Result): newSite = RSSSite(RSSDlg.RSSName , RSSDlg.RSSUrl) self.lbSites.insert(END, RSSDlg.RSSName) self.lbSites.select_clear(0,END) self.lbSites.select_set(END) self.lstSites.append(newSite)
So we create the new dialog, and then if the user clicks Ok (Result == True) we create a new site. Then we add the sites name to the end of the lbSites listbox, clear any current selection, and then select what we just added. After that we simply add the RSS site to our site list. So the position of the RSS Sites name in the lbSites ListBox corresponds to the RSSSite’s position in the lstSites list. It’s basically like a simple mapping, I would rather store all of the information in the Listbox but I couldn’t find anyway to do that.
We will use that same basic formula to create the EditRSSItem function, but instead of creating a new RSSSite we need to edit the selected RSSSite:
def EditRSSItem(self): """Edit an RSSItem""" """Get the Current selection""" lstCurrSel = self.lbSites.curselection(); if (len(lstCurrSel)>0): selected_site = self.lstSites[int(lstCurrSel)] RSSDlg = RSSItemDialog(self ,"Edit" , selected_site.Name , selected_site.Url) if (RSSDlg.Result): """Update the ListBox""" self.lbSites.delete(lstCurrSel) self.lbSites.insert(lstCurrSel, RSSDlg.RSSName) self.lbSites.select_clear(0,END) self.lbSites.select_set(lstCurrSel) """Save the values in the list""" self.lstSites[int(lstCurrSel)] = RSSSite(RSSDlg.RSSName , RSSDlg.RSSUrl)
The only confusing code that you might see is this int(lstCurrSel), which is basically converting the first value in the selection list to an integer. We need to do this because list in python expect integers for indices. So we get the current selected index in the ListBox and then use that to get the RSSSite from our list. Then the rest of the process is almost identical to the AddRSSItem function.
Now we need to hook the two functions up to our buttons:
self.btnAdd = Button(self, text="Add", command=self.AddRSSItem) self.btnEdit = Button(self, text="Edit", command=self.EditRSSItem)
You can probably guess what the next step in the equation is, we need to make it so that our View button displays all of the RSS items in the lbRSSItems ListBox. To do this we are going to need to bring in the RSSGenerator.py code that we created in part three. Instead of taking all the code from that file and then adding it to our current file (which we could do) we are simple going to move that file into the same directory as our current python file (mine is called GUI.py) then we have to import or include the RSSGenerator.py classes:
from RSSGenerator import *
Then we have to create a ViewRSSItems function, which is very similar to our EditRSSItem function except that it will fill the lbRSSItems ListBox with all of the current RSS items available on the feed:
def ViewRSSItems(self): """Get the Current selection""" lstCurrSel = self.lbSites.curselection(); if (len(lstCurrSel)>0): selected_site = self.lstSites[int(lstCurrSel)] """Empty the ListBox""" self.lbRSSItems.delete(0, END) rss_reader = RSSReader(selected_site.Url) for rss_item in rss_reader.GetItems(): if (rss_item): self.lbRSSItems.insert(END,rss_item.title) self.lstItems.append(rss_item)
So we get the current selection, then we create our RSSReader based upon the RSSSites Url. Then we loop through the RSSItems adding each to our ListBox and to our list of the items (lstItems) that we have added in out GUIFramework’s __init__ function:
self.lstItems = 
As you can see the RSS Reader is actually starting to take shape, now all we have to do is show the RSS Items description in the text widget when we click the Set Text button. Given all the ListBox processing that we have done recently the way that this works should come as no surprise:
def SetStoryText(self): """Set the Story text, called form the btnSetText""" """Get the Current selection""" lstCurrSel = self.lbRSSItems.curselection(); if (len(lstCurrSel)>0): rss_item = self.lstItems[int(lstCurrSel)] """Set the Text Widgets text""" self.txtItem.config(state=NORMAL) self.txtItem.delete(1.0,END) self.txtItem.insert(INSERT, rss_item.description + "\r\n\r\n") self.txtItem.insert(INSERT, rss_item.link, "a") self.txtItem.config(state=DISABLED)
Whew that’s a lot of code this time, but none of it is really that new or that difficult if you have been following along with the RSS Reader so far. You might notice that I have not gone into great detail describing all of the code. The reason for that is because for the most part I’ve already gone over the code in detail in a previous post, or the code is sufficiently similar to code I have gone over that I don’t think that it needs a great deal of description. But if you have any questions about any of the code that I have shown above feel free to ask a question and I will be happy to explain it in more detail.
The next step in the RSS Read will be to save and load the RSSSites into a file so that we don’t have to waste our time typing them in every time we start the program, but I’ll leave that for another day.
You can download all the code used in this post here: RSSPart4.zip