Saturday, August 12, 2006

A Windows.Forms GUI for the GDATA Reader using IronPython

This is the second coding entry in a series of posts about using IronPython to develop a GDATA reader.

The purpose of this post is show how to add a GUI to the simple GDATA reader IronPython script I created in a previous post using System.Windows.Forms. It will not be an in depth discussion on how things work within Windows Forms. For a more detailed introduction to Windows Forms and IronPython, see Michael Foords excellent series of posts.

I suggest you open the source for the complete script in another browser window before continuing.

For the GUI we need a form and on the form, a textbox widget to enter the GDATA URI, a multi-line textbox widget to display the title and summary of each returned feed entry or any errors, a statusbar, and a button to re-fetch the feed.

To the original gdatareader.py script, we add references to the System.Windows.Forms and System.Drawing assemblies and import them.

Next we create a form class called GDataReaderForm.To design the form, I find it's easier to use the GUI Form Designer in Visual Studio 2003/2005 or Visual C# Express. I then copy the generated C# code for the form and with a couple of minutes of search/replace ( this to self, remove those pesky semi-colons and casts) and reformatting, I have the Python code to display my form. This is the _initgui method in the GDataReaderForm class.

Rather than explaining every widget property, I will just point out the things that I had to spend some effort on to get things to work.
  • If the form is resized, we want the widgets to scale in relation to the re-sizing, so you need to set the Anchor property.
  • If the Enter key is pressed, we want the program to fetch the URI, you need to set the AcceptsReturn property of the URI textbox to false. This means the Enter key with then activate the default button for the form. To make the Refresh button the default button, you need to set the AcceptButton property of the form.
self.AcceptButton = self.gdataRefreshButton

Attached to the Click event of the Refresh button is the method GdataLoadFeed.

self.gdataRefreshButton.Click += self.GdataLoadFeed

So when the Enter key is pressed or the Refresh button clicked, GdataLoadFeed is called, which inturn calls load_feed which finally calls parse the function created in the previous post to fetch and parse a GDATA feed. A list of entries is returned, formatted and displayed in the textbox.

The following screenshot shows the completed GUI displaying the Atom 1.0 feed from Jim Hugunin's blog running under Windows 2003 Terminal Server client (which explains the lack of a XP theme). The Atom feeds from blogs.msdn.com do not have a summary element so our script displays a blank line.



And the next screenshot is the same code running the GUI on Ubuntu 6.06 LTS with Mono 1.1.16.1.


Improving the feed display with a RTF Textbox

Since a GDATA entry contains a link element it would be useful if it was displayed and a user could activate the link to view the entry content in a browser. Some more investigation of the gdata.dll was required to discover how to identify and access the link. The following function does the job for the GDATA reader:

def GetRelatedUri(self, entry, reltype="alternate"):
'''
Get the related uri of reltype from the GDATA entry Links collection.
Returns Uri or None if not found.
'''
uri = None
for link in entry.Links:
if hasattr(link, "Rel"):
if link.Rel == reltype:
uri = link.HRef.Content
else:
if reltype == "alternate":
# No Rel attribute means it's alternate
uri = link.HRef.Content
return uri
I discovered a RichTextBox widget will detect any urls in it's text if the DetectUrls property is set to true. So the the feed display TextBox was replaced with a RichTextBox, and the formatting function for the entry display replaced with a function that created RTF text. To handle a link being clicked the LinkClick event must have a function assigned to it.

self.gdataEntriesRichTextBox.LinkClicked += self.LinkClicked

The code for the event handler:

def LinkClicked(self, sender, args):
'''
Get OS to launch the link using associated application
'''
System.Diagnostics.Process.Start(args.LinkText)

The full source can be found here. The following screenshot shows the completed GUI with RichTextBox displaying the Atom 1.0 feed under Windows 2003 Terminal Server.


The functionality of the RichTextBox widget with the current Mono version of Windows.Forms means that the text formatting is not the same as .NET and the URL's are displayed but are not clickable. Hopefully this will be fixed soon.

The GUI is missing a couple of features like a top menu and About dialog, maybe that's something for another post.

In the next post we will compile the program into a CLI executable.

1 comment:

Steven said...

I’m trying to find out about Unified Communication for a project but there doesn’t seem to be much information available. Is it the same as VoIP, and if not how is it different?