<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>learning python &#187; glade</title>
	<atom:link href="http://www.learningpython.com/category/python/glade/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.learningpython.com</link>
	<description>one man's journey into python...</description>
	<lastBuildDate>Mon, 26 Apr 2010 01:21:51 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=abc</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>PyLan a GTD todo application written in python and PyGTK &#8211; part three</title>
		<link>http://www.learningpython.com/2007/07/04/pylan-a-gtd-todo-application-written-in-python-and-pygtk-part-three/</link>
		<comments>http://www.learningpython.com/2007/07/04/pylan-a-gtd-todo-application-written-in-python-and-pygtk-part-three/#comments</comments>
		<pubDate>Thu, 05 Jul 2007 01:20:31 +0000</pubDate>
		<dc:creator>selsine</dc:creator>
				<category><![CDATA[PyGTK]]></category>
		<category><![CDATA[PyLan]]></category>
		<category><![CDATA[glade]]></category>
		<category><![CDATA[gui]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://www.learningpython.com/?p=62</guid>
		<description><![CDATA[
			
				
			
		
PyLan three
Hello welcome to the long-time-coming third article in this tutorial.  I apologize to everyone (anyone?) that was waiting for it.  I have been very busy as of late and have had much of my time taken up by a few other python projects, that I hope to be able to show you [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.learningpython.com%2F2007%2F07%2F04%2Fpylan-a-gtd-todo-application-written-in-python-and-pygtk-part-three%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.learningpython.com%2F2007%2F07%2F04%2Fpylan-a-gtd-todo-application-written-in-python-and-pygtk-part-three%2F&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<h2>PyLan three</h2>
<p>Hello welcome to the long-time-coming third article in this tutorial.  I apologize to everyone (anyone?) that was waiting for it.  I have been very busy as of late and have had much of my time taken up by a few other python projects, that I hope to be able to show you all soon.</p>
<p>If you want to follow along with the code in detail, and have not done so already, you should read <a href="http://www.learningpython.com/2007/02/17/pylan-a-gtd-todo-application-written-in-python-and-pygtk-part-one/">part one</a> and <a href="http://www.learningpython.com/2007/02/17/pylan-a-gtd-todo-application-written-in-python-and-pygtk-part-two/">part two</a> of this series. </p>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/pylan_03/pylan_03_01.png" alt="Python GTD pyGTK" border="0"/></p>
<p>You can download the full source to this tutorial <a href="http://www.learningpython.com/sources/PyLan_03.tar.gz">here</a>.</p>
<p>This tutorial will teach you the following things:</p>
<ol>
<li>How to construct a simple theme engine, or at least how I would, hopefully it will give you some ideas!</li>
<li>How to display icons in a gtk.TreeView</li>
<li>How to catch the selection event in a gtk.TreeView.</li>
<li>How to enable or disable widgets.</li>
<li>How to remove the selection from a gtk.TreeView if the user clicks on the gtk.TreeView but not on a tree item.</li>
</ol>
<p><span id="more-62"></span><br />
This tutorial is organized into the following sections:</p>
<ol>
<li><a href="#Theme">A Simple Theme Engine</a></li>
<li><a href="#ShowingIcons">Showing Icons</a></li>
<li><a href="#EnablingWidgets">Enabling and Disabling Widgets</a></li>
<li><a href="#RemoveSelection">Removing Unwanted Selection</a></li>
<li><a href="#Conclusion">Conclusion</a></li>
</ol>
<p>As with other tutorial in this series this will not go into all details and changes that were made in the code, instead it will deal with a few main subjects that will hopefully provide ideas and sample code to others interested in python and PyGtk programming.</p>
<h2><a href="#Theme">A Simple Theme Engine</a></h2>
<p>Now a while back I had an interesting idea that I would like to create a simple theme engine for the PyLan application.  I thought that it would be easy and let me (or other people) change the icons or colours that are used.</p>
<p>So my idea was to use a dictionary object indexed by constant keys, whose values would be &#8220;theme objects.&#8221;</p>
<p>I thought that this would be a simple enough idea that I could implement quickly and then expand in the future to have more features like the ability to save and load the theme from xml.</p>
<p>So to start we create a file called theme.py and filled it with all of the basics and some imports and some constants:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">try</span><span class="hl-default">:
	</span><span class="hl-reserved">import </span><span class="hl-identifier">gtk
	</span><span class="hl-reserved">import </span><span class="hl-identifier">helper
</span><span class="hl-reserved">except</span><span class="hl-default">:
  	</span><span class="hl-reserved">print </span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">import Error</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
  	
</span><span class="hl-identifier">PIXMAP</span><span class="hl-default"> = </span><span class="hl-number">0
</span><span class="hl-identifier">COLOUR</span><span class="hl-default"> = </span><span class="hl-number">1</span></pre></div></div>
<p>Next we create our Theme object that will serve as the applications theme.  In this version the Theme object is still very simple, here is it in its entirety:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">class </span><span class="hl-identifier">Theme</span><span class="hl-brackets">(</span><span class="hl-identifier">dict</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This object represents all of the necessary information needed
	to properly present information in a pyLan application...or any
	application
	</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">theme_dict</span><span class="hl-code"> = </span><span class="hl-reserved">None</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">initialize the theme
		@param theme_dict - dictionary of values.  If None it is
		ignored, if not none, it's values will be stored in the internal
		theme_dictionary.</span><span class="hl-quotes">&quot;&quot;&quot;

		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">load_from_dictionary</span><span class="hl-brackets">(</span><span class="hl-identifier">theme_dict</span><span class="hl-brackets">)

	</span><span class="hl-reserved">def </span><span class="hl-identifier">load_from_dictionary</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">theme_dict</span><span class="hl-code">, </span><span class="hl-identifier">initialize</span><span class="hl-code"> = </span><span class="hl-reserved">False</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function loads all of the theme values
		from the theme_dict into the internal theme dictionary.
		@param theme_dict - dictionary - A theme dictionary that
		describes theme items.
		@param initialize = False - boolean, do we want to blank
		out the internal dictionary before we load the items?
		</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-comment">#Make sure that the dict is not None
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-reserved">not </span><span class="hl-identifier">theme_dict</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-reserved">return
		</span><span class="hl-comment">#clear yourself out
		</span><span class="hl-identifier">dict</span><span class="hl-default">.</span><span class="hl-identifier">clear</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)

		</span><span class="hl-reserved">for </span><span class="hl-identifier">key</span><span class="hl-default">,</span><span class="hl-identifier">value </span><span class="hl-reserved">in </span><span class="hl-identifier">theme_dict</span><span class="hl-default">.</span><span class="hl-identifier">iteritems</span><span class="hl-brackets">()</span><span class="hl-default">:
			</span><span class="hl-identifier">self</span><span class="hl-brackets">[</span><span class="hl-identifier">key</span><span class="hl-brackets">]</span><span class="hl-default"> = </span><span class="hl-identifier">value</span></pre></div></div>
<p>So what is happening here?  Not that much, the Theme object uses the dict object as a base class and then lets you initialize the Theme using a dictionary, which is done using a helper function.  This was put for future usage and is not used yet.</p>
<p>The next item in the Theme setup is the ThemeItem object.  This is the object that will be used as the value for each item in our Theme dictionary.</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre>class ThemeItem(object):
	&quot;&quot;&quot;An item in the theme.  This is used to store all of the
	necessary theme information.
	&quot;&quot;&quot;

	&quot;&quot;&quot;Type property - Cannot be set, only set in the
	initializer.&quot;&quot;&quot;
	def __get_type(self):
		return self._m_type
	type = property(__get_type)

	&quot;&quot;&quot;Data Property&quot;&quot;&quot;
	def __get_data(self):
		return self._m_data
	data = property(__get_data)

	def __init__(self, type):
		&quot;&quot;&quot;Initialize yourself.
		@param type - number - The type of theme item.
		&quot;&quot;&quot;
		self._m_type = type
		self._m_data = None
		#now load the data
		self.load_data()

	def load_data(self):
		&quot;&quot;&quot;This function is call when the Theme item
		should load any data that it needs. i.e. if it
		is a pixmap it should load the image file.
		it should create the data.
		&quot;&quot;&quot;
		pass</pre></div></div>
<p>First you can see that there are two date members in the ThemeItem base class, the _m_type and _m_data.  The _m_type is simply used to let interested parties know what the type of theme object is.  I&#8217;m not sure if the _m_type is still needed, originally I was using it but after changing a few things it doesn&#8217;t really do much (anything?) anymore.</p>
<p>The next data member is that _m_data item that is meant to be private and accessed through the data property.  As you can see the data property cannot be set it can only be read.  This was done this way so that only the ThemeItem would be able to set its data.</p>
<p>Now the data member can be anything, it could be an image, a colour, whatever you want it to be.  The data member will generally be set by a subclass of the ThemeItem.  You&#8217;ll also notice that the data is &#8220;loaded&#8221; in the __init__ function.  By default the load_data() function does nothing, and should be overridden in the base class.</p>
<p>The only type that I have in use so far is the ThemePixmap object.  This is a ThemeItem that sets its data to be a <a href="http://www.pygtk.org/docs/pygtk/class-gdkpixbuf.html">gtk.gdk.pixbuf</a>:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">class </span><span class="hl-identifier">ThemePixmap</span><span class="hl-brackets">(</span><span class="hl-identifier">ThemeItem</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">A Them item that represents a pixmap to be displayed.</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">file_name</span><span class="hl-code"> = </span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Initialize the pixmap theme item.
		@param file_name - string - the path to the
		pixmap file.
		</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">file_name</span><span class="hl-default"> = </span><span class="hl-identifier">file_name
		ThemeItem</span><span class="hl-default">.</span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">PIXMAP</span><span class="hl-brackets">)

	</span><span class="hl-reserved">def </span><span class="hl-identifier">load_data</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This is where we load the pixmap based on the file name</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-reserved">try</span><span class="hl-default">:
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">_m_data</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">gdk</span><span class="hl-default">.</span><span class="hl-identifier">pixbuf_new_from_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">file_name</span><span class="hl-brackets">)
		</span><span class="hl-reserved">except </span><span class="hl-identifier">gerror</span><span class="hl-default">, </span><span class="hl-identifier">e</span><span class="hl-default">:
			</span><span class="hl-identifier">helper</span><span class="hl-default">.</span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Error loading pixmap %s = %s</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">
				% </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">file_name</span><span class="hl-code">, </span><span class="hl-identifier">e</span><span class="hl-brackets">))
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">_m_data</span><span class="hl-default"> = </span><span class="hl-reserved">None</span></pre></div></div>
<p>Pretty straightforward here, a simple subclass of the ThemeItem object that overrides the load_data() function and adds a new class member.  The load_data function simply loads a pixmap into the data using the <a href="http://www.pygtk.org/docs/pygtk/class-gdkpixbuf.html#function-gdk--pixbuf-new-from-file">gtk.gdk.pixbuf_new_from_file</a> function.</p>
<p>Now how do we use this?</p>
<p>In the main pyLan.py file I added the following function to initalize the theme, it is called from the __init__ function and could theoretically be called from other locations:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">initialize_theme</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is used to initialise the
	theme.</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">app_theme</span><span class="hl-default"> = </span><span class="hl-identifier">theme</span><span class="hl-default">.</span><span class="hl-identifier">Theme</span><span class="hl-brackets">()
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">app_theme</span><span class="hl-brackets">[</span><span class="hl-identifier">CATEGORY_ICON</span><span class="hl-brackets">]</span><span class="hl-default"> = </span><span class="hl-identifier">theme</span><span class="hl-default">.</span><span class="hl-identifier">ThemePixmap</span><span class="hl-brackets">(
		</span><span class="hl-identifier">os</span><span class="hl-code">.</span><span class="hl-identifier">path</span><span class="hl-code">.</span><span class="hl-identifier">join</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">pixmaps</span><span class="hl-code">, </span><span class="hl-quotes">&quot;</span><span class="hl-string">stock_book_blue.png</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">))
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">app_theme</span><span class="hl-brackets">[</span><span class="hl-identifier">TASK_ICON</span><span class="hl-brackets">]</span><span class="hl-default"> = </span><span class="hl-identifier">theme</span><span class="hl-default">.</span><span class="hl-identifier">ThemePixmap</span><span class="hl-brackets">(
		</span><span class="hl-identifier">os</span><span class="hl-code">.</span><span class="hl-identifier">path</span><span class="hl-code">.</span><span class="hl-identifier">join</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">pixmaps</span><span class="hl-code">, </span><span class="hl-quotes">&quot;</span><span class="hl-string">stock_task.png</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">))</span></pre></div></div>
<p>So this is how I use the theme in the main PyLan application, I&#8217;m using it to determine what icon to display for tasks and what icon to use for categories.</p>
<p>As you can see I simply create a theme member in the PyLan class and the add two ThemePixmap items to the Theme, each index by constants that will be used to identify them later.  In the future I hope to use this function to load the information from a file.</p>
<p>The constants used in the function are defined as follows:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-identifier">CATEGORY_ICON</span><span class="hl-default"> 			= </span><span class="hl-number">1
</span><span class="hl-identifier">TASK_ICON</span><span class="hl-default">				= </span><span class="hl-number">2</span></pre></div></div>
<p>I will show you how the theme is used in the next section.</p>
<h2><a name="ShowingIcons"></a>Showing Icons</h2>
<p>So now that we have a simple theme engine that load graphics for us we need to figure out how to show images in our gtk.TreeView.</p>
<p>What we will do is we will use the <a href="http://www.pygtk.org/docs/pygtk/class-gtkcellrendererpixbuf.html">gtk.CellRendererPixbuf</a> to render our image.  We will also pack the image into the same column as the Title column.  We do this so that the icon isn&#8217;t in a column of it&#8217;s own, which would seem quite strange.</p>
<p>Basically what we will do is create the gtk.TreeViewColumn in our normal way, using the gtk.CellRenberPixbuf, then when we use the <a href="http://www.pygtk.org/docs/pygtk/class-gtktreeviewcolumn.html#method-gtktreeviewcolumn--pack-start">gtk.TreeViewColumn.pack_start</a> function to add the <a href="http://www.pygtk.org/docs/pygtk/class-gtkcellrenderertext.html">gtk.CellRendererText</a> (representing the Title Text) to add the text to column.  This means that we will have two cell renderers in the same column.</p>
<p>The actual code to accomplish this in the pyLan program is quite complicated as I attempted to use the todoColumn object as a generic way to order the columns.  This worked fine before but now it has become a more complicated and slightly hackish now that we want to be able to pack items into the same column.</p>
<p>Sufficed to say I accomplished this by adding a new share_column member to the totoColum that allows you to Pack Widgets.</p>
<p>We also have to update the get_column_list() function in the todo.Task and todo.Category items so that they returns the pixbuf from the theme.  This also means that we need to add a new member to the todo.todoBase object:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">type</span><span class="hl-code">, </span><span class="hl-identifier">app_theme</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">The only way to set the type is through the
	initialization</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_type</span><span class="hl-default"> = </span><span class="hl-identifier">type
	self</span><span class="hl-default">.</span><span class="hl-identifier">app_theme</span><span class="hl-default"> = </span><span class="hl-identifier">app_theme</span></pre></div></div>
<p>Where app_theme is an instance of our Theme object described in the previous section.</p>
<p>Then in the get_column_list() the todo.Category objects need to add the following:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-identifier">elif </span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-code"> == </span><span class="hl-identifier">pyLan</span><span class="hl-code">.</span><span class="hl-identifier">COL_ICON</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">app_theme</span><span class="hl-brackets">[</span><span class="hl-identifier">pyLan</span><span class="hl-code">.</span><span class="hl-identifier">CATEGORY_ICON</span><span class="hl-brackets">]</span><span class="hl-code">.</span><span class="hl-identifier">data</span><span class="hl-brackets">)</span></pre></div></div>
<p>and the todo.Theme object needs to add the following:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-identifier">elif </span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-code"> == </span><span class="hl-identifier">pyLan</span><span class="hl-code">.</span><span class="hl-identifier">COL_ICON</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">app_theme</span><span class="hl-brackets">[</span><span class="hl-identifier">pyLan</span><span class="hl-code">.</span><span class="hl-identifier">TASK_ICON</span><span class="hl-brackets">]</span><span class="hl-code">.</span><span class="hl-identifier">data</span><span class="hl-brackets">)</span></pre></div></div>
<p>Pretty simple, all we do is we get that Theme item in the applications theme and then return its data.  If this seems strange or you cannot understand it, please take a look at the previous tutorial and you you will be able to understand what the get_column_list() function does.</p>
<h2><a name="EnablingWidgets"></a>Enabling and Disabling Widgets</h2>
<p>One of the things that has been bothering me about the PyLan application was that all of the buttons on the toolbar were always enabled even when they cannot perform their function.</p>
<p>Now I&#8217;m used to the Enable/Disabled terminology except in GTK the terms sensitive and insensitive are used, so I will try to use them from now on, but if an enabled or a disabled slips in there please forgive me.</p>
<p>The first thing that we need to add is an initialize_widgets() function to the PyLan class, this function will gather widgets that we need and will set their default state.  This needs to be called early in the __init__ function before initialize_todo_tree() is called.  The function is as follows:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">initialize_widgets</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Initialize any widgets that we want.  Basically
	grab widgets that you want to have access to later
	on in the program.
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-comment">#Get the widgets
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">edit_button</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">btnEdit</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">delete_button</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">btnRemoveCategory</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">add_task_button</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">btnAddTask</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-comment">#disable them
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">edit_button</span><span class="hl-default">.</span><span class="hl-identifier">set_sensitive</span><span class="hl-brackets">(</span><span class="hl-reserved">False</span><span class="hl-brackets">)
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">delete_button</span><span class="hl-default">.</span><span class="hl-identifier">set_sensitive</span><span class="hl-brackets">(</span><span class="hl-reserved">False</span><span class="hl-brackets">)
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">add_task_button</span><span class="hl-default">.</span><span class="hl-identifier">set_sensitive</span><span class="hl-brackets">(</span><span class="hl-reserved">False</span><span class="hl-brackets">)</span></pre></div></div>
<p>The code is pretty simple, we grab the widgets from the widget tree, and then we simply call the <a href="http://www.pygtk.org/docs/pygtk/class-gtkwidget.html#method-gtkwidget--set-sensitive">set_sensitive</a> function and tell them to be insensitive by passing False to the function.</p>
<p>The next step is to connect the application to the <a href="http://www.pygtk.org/docs/pygtk/class-gtktreeselection.html#signal-gtktreeselection--changed">&#8220;changed&#8221; signal</a> that is emitted by the <a href="http://www.pygtk.org/docs/pygtk/class-gtktreeselection.html">gtk.TreeSelection</a> that is associated with our gtk.TreeView.  To do so we need to add the following code to the initialize_todo_tree() function:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-comment">#Enable the selection callback
</span><span class="hl-identifier">selection</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">todoTreeView</span><span class="hl-default">.</span><span class="hl-identifier">get_selection</span><span class="hl-brackets">()
</span><span class="hl-identifier">selection</span><span class="hl-default">.</span><span class="hl-identifier">connect</span><span class="hl-brackets">(</span><span class="hl-quotes">'</span><span class="hl-string">changed</span><span class="hl-quotes">'</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">on_tree_selection_changed</span><span class="hl-brackets">)</span></pre></div></div>
<p>Pretty simple, we get the gtk.TreeSelection from the gtk.TreeView and then we connect the on_tree_selection_changed() function with the changed signal.</p>
<p>The on_tree_selection_changed() function is relatively simple as well:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_tree_selection_changed</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">selection</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the selection has changed in the
	tree.
	@param selection - gtk.TreeSelection - The selection
	object associated with the gtk.TreeView.
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">model</span><span class="hl-default">, </span><span class="hl-identifier">selection_iter</span><span class="hl-default"> = </span><span class="hl-identifier">selection</span><span class="hl-default">.</span><span class="hl-identifier">get_selected</span><span class="hl-brackets">()
	</span><span class="hl-comment">#If there is a selection then enable these
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">edit_button</span><span class="hl-default">.</span><span class="hl-identifier">set_sensitive</span><span class="hl-brackets">((</span><span class="hl-identifier">selection_iter</span><span class="hl-code"> != </span><span class="hl-reserved">None</span><span class="hl-brackets">))
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">delete_button</span><span class="hl-default">.</span><span class="hl-identifier">set_sensitive</span><span class="hl-brackets">((</span><span class="hl-identifier">selection_iter</span><span class="hl-code"> != </span><span class="hl-reserved">None</span><span class="hl-brackets">))
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">add_task_button</span><span class="hl-default">.</span><span class="hl-identifier">set_sensitive</span><span class="hl-brackets">((</span><span class="hl-identifier">selection_iter</span><span class="hl-code"> != </span><span class="hl-reserved">None</span><span class="hl-brackets">))</span></pre></div></div>
<p>First we get the selection_iter from the gtk.TreeSelection and then we enable the edit, delete, and add_task buttons based on whether or not there is a selection in the gtk.TreeView.  If nothing is selected then we make the buttons insensitive.</p>
<h2><a href="#RemoveSelection">Removing Unwanted Selection</a></h2>
<p>One feature that I wanted to implement in the application was the ability to remove selection from the tree by clicking on an empty space in the tree.  As far as I can tell there is no simple method to enable a feature like this in PyGTK, which I found quite surprising.</p>
<p>As a result what I needed to do was connect to the gtk.TreeView&#8217;s <a href="http://www.pygtk.org/docs/pygtk/class-gtkwidget.html#signal-gtkwidget--button-press-event">button-press-event</a>.  Then we use the location where the user clicked and see if they clicked on an item in the tree.  If they did not click on an item in the tree then we remove the selection from the tree.  If they did click on something then we leave the selection as is.</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_todoTree_button_press_event</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-code">, </span><span class="hl-identifier">event</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">There has been a button press on the TodoTree
	for now we use this as a quick hack to remove
	the selection.  Perhaps there is a better way?
	@param widget - gtk.TreeView - The Tree View
	@param event - gtk.gdk.event - Event information
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-comment">#Get the path at the specific mouse position
	</span><span class="hl-identifier">path</span><span class="hl-default"> = </span><span class="hl-identifier">widget</span><span class="hl-default">.</span><span class="hl-identifier">get_path_at_pos</span><span class="hl-brackets">(</span><span class="hl-builtin">int</span><span class="hl-brackets">(</span><span class="hl-identifier">event</span><span class="hl-code">.</span><span class="hl-identifier">x</span><span class="hl-brackets">)</span><span class="hl-code">, </span><span class="hl-builtin">int</span><span class="hl-brackets">(</span><span class="hl-identifier">event</span><span class="hl-code">.</span><span class="hl-identifier">y</span><span class="hl-brackets">))
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">path</span><span class="hl-code"> == </span><span class="hl-reserved">None</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">If we didn't get apath then we don't want anything
		to be selected.</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">selection</span><span class="hl-default"> = </span><span class="hl-identifier">widget</span><span class="hl-default">.</span><span class="hl-identifier">get_selection</span><span class="hl-brackets">()
		</span><span class="hl-identifier">selection</span><span class="hl-default">.</span><span class="hl-identifier">unselect_all</span><span class="hl-brackets">()</span></pre></div></div>
<p>As you can see, we take position of the cursor where the button was pressed and then call the <a href="http://www.pygtk.org/docs/pygtk/class-gtktreeview.html#method-gtktreeview--get-path-at-pos">get_path_at_pos()</a> function in the gtk.TreeView which returns a tuple containing information if an item in the tree can be found at the specified x,y position, and None if nothing in the tree was hit.</p>
<p>It&#8217;s not pretty but it works!</p>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/pylan_03/pylan_03_01.png" alt="Python GTD pyGTK" border="0"/></p>
<p>You can download the full source to this tutorial <a href="http://www.learningpython.com/sources/PyLan_03.tar.gz">here</a>.</p>
<h2><a name="Conclusion"></a>Conclusion</h2>
<p>Now one of the problems that I had writing this tutorial is that it&#8217;s getting very hard to detail all of the changes that are made to the project.  So I have tried to leave out as much extraneous detail as I possibly could, but then I start to wonder how much use anyone could get out of these articles if the details are left out.</p>
<p>Hopefully there is still enough in there so that people can get ideas and at least some hints as to how things work in some of the slightly more advanced areas of PyGTK.</p>
<p>I&#8217;m not sure if I am going to continue detailing everything in this test application, I might skip a few phases since detailing everything is getting to be more of a problem.  But what I really want to focus on soon is working with things like Preferences and distributions.  Sadly time is always a factor!</p>
<p>If you have any questions please ask away as always!</p>
<div style="float:right;margin:0px 0px 0px 0px;"><a href="http://www.google.com/reader/link?url=http://www.learningpython.com/2007/07/04/pylan-a-gtd-todo-application-written-in-python-and-pygtk-part-three/&title=PyLan a GTD todo application written in python and PyGTK - part three&srcTitle=learning python&srcURL=http://www.learningpython.com"target="_blank" rel=""><img border="0" src="http://www.learningpython.com/wp-content/plugins/wp-google-buzz/icon/12.png" style="opacity:1;filter:alpha(opacity=100)" onmouseover="this.style.opacity=0.8;this.filters.alpha.opacity=70" onmouseout="this.style.opacity=1;this.filters.alpha.opacity=100"/> </a></div>]]></content:encoded>
			<wfw:commentRss>http://www.learningpython.com/2007/07/04/pylan-a-gtd-todo-application-written-in-python-and-pygtk-part-three/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>PyLan a GTD todo application written in python and PyGTK &#8211; part two</title>
		<link>http://www.learningpython.com/2007/03/11/pylan-a-gtd-todo-application-written-in-python-and-pygtk-part-two/</link>
		<comments>http://www.learningpython.com/2007/03/11/pylan-a-gtd-todo-application-written-in-python-and-pygtk-part-two/#comments</comments>
		<pubDate>Sun, 11 Mar 2007 22:48:07 +0000</pubDate>
		<dc:creator>selsine</dc:creator>
				<category><![CDATA[PyGTK]]></category>
		<category><![CDATA[PyLan]]></category>
		<category><![CDATA[glade]]></category>
		<category><![CDATA[gui]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://www.learningpython.com/?p=58</guid>
		<description><![CDATA[
			
				
			
		
PyLan two
This is part two of the PyLan tutorial series, if you want to follow along with the code in detail, and have not done so already, you should read part one of this series. 
In this tutorial I will go over the following items:

Showing a popup window
Working with a gtk.Calendar widget
Working with gtk.ComboBox widgets
Working [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.learningpython.com%2F2007%2F03%2F11%2Fpylan-a-gtd-todo-application-written-in-python-and-pygtk-part-two%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.learningpython.com%2F2007%2F03%2F11%2Fpylan-a-gtd-todo-application-written-in-python-and-pygtk-part-two%2F&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<h2>PyLan two</h2>
<p>This is part two of the PyLan tutorial series, if you want to follow along with the code in detail, and have not done so already, you should read <a href="http://www.learningpython.com/2007/02/17/pylan-a-gtd-todo-application-written-in-python-and-pygtk-part-one/">part one</a> of this series. </p>
<p>In this tutorial I will go over the following items:</p>
<ul>
<li>Showing a popup window</li>
<li>Working with a <a href="http://www.pygtk.org/docs/pygtk/class-gtkcalendar.html">gtk.Calendar</a> widget</li>
<li>Working with <a href="http://www.pygtk.org/docs/pygtk/class-gtkcombobox.html">gtk.ComboBox</a> widgets</li>
<li>Working with Pango markup in a gtk.TreeView</li>
<li>Working with <a href="http://www.pygtk.org/docs/pygtk/class-gtkcheckbutton.html">gtk.CheckButton</a> widgets</li>
<li>Working with the <a href="http://docs.python.org/lib/module-datetime.html">DateTime</a> python module.</li>
</ul>
<p>This tutorial is organized into the following sections:</p>
<ol>
<li><a href="#TheGUI">The GUI</a></li>
<li><a href="#todoTask">The todo.Task Object</a></li>
<li><a href="#addedittodoTask">Adding a todo.Task object</a></li>
<li><a href="#calendarWindow">Showing the Calendar window</a></li>
<li><a href="#EditingItems">Editing todo.Category and todo.Task items</a></li>
<li><a href="#PangoMarkup">Pango markup in the gtk.TreeView</a></li>
<li><a href="#Conclusion">Conclusion</a></li>
</ol>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/pylan_01/pylan_01_08.png" alt="Python GTD pyGTK" border="0" /></p>
<p>You can download the full source to this tutorial <a href="http://www.learningpython.com/sources/PyLan_02.tar.gz">here</a>.</p>
<p><span id="more-58"></span></p>
<h2><a name="TheGUI"></a>The GUI</h2>
<p>I will be working with the same glade project as last time, except I will be adding a some new dialogs and buttons.  I will not go over all of the changes in the GUI since many of them are very simple changes, similar to changes that have been made in previous tutorials.  </p>
<p>For example I decided to get rid of the Dropdown menu for adding the Categories and the tasks, instead I decided to create separate buttons to do the two separate tasks.  I won&#8217;t describe the switch here, since adding toolbar buttons is pretty straightforward.  For anyone interested in why I decided to make this switch, the answer is pretty simple, I didn&#8217;t like using the drop down menu as much as I thought that I would.  Instead I found it confusing and prone to error.  As a result I made the switch to the separate buttons. </p>
<p>So here is a non-comprehensive list of the changes that I made to the GUI in case you were interested in seeing how something was done.</p>
<p><strong>The Task dialog</strong></p>
<p>This dialog is used to add or edit a todo.Task object.</p>
<ul>
<li>Create a Dialog with an Ok and a Cancel button.  Sets it&#8217;s name to be &#8220;taskItemDialog&#8221;, and its title to be &#8220;Task Item&#8221;.  Give it a width of 300 and a height of 225.</li>
<li>Add a Vertical Box to the dialog with 2 rows.  In the second row add a Check Button with the label &#8220;Completed&#8221; and the name &#8220;checkCompleted&#8221;.</li>
<li>In the first row add a table with two columns and four rows.  To the following to the table, adding vertical padding of 3 pixels to each space in the second column:
<ul>
<li>Add a label with the label &#8220;Title&#8221; in [0, 0].</li>
<li>Add a Text Entry in [0, 1] and call it enTitle</li>
<li>Add a label with the label &#8220;Details&#8221; in [1, 0], give it three pixel of horizontal padding.</li>
<li>Add a Text View in [1, 1] and call it tvDetails.  In the scrolled window&#8217;s properties set the H Policy to &#8220;Never&#8221; and the V Policy to &#8220;Automatic&#8221;.  This means that there will never be a horizontal scrollbar, and that there will be a vertical scrollbar only when one is necessary.</li>
<li>Add a label in [2, 0] with the Label &#8220;Due:&#8221;.</li>
<li>Add a horizontal box with three columns in [2, 1].  In the first add an entry labelled enDueDate, in the second add a button named btnDue add a clicked signal handler, and in the third add a combobox and give it the name &#8220;cmbTime&#8221;.  Add time entries to the combobox in the form: &#8220;&#8221;(blank),12:00AM, 12:30AM, 01:00AM&#8230;11:30PM.  Then right-click on the button and select it in the menu and then select &#8220;Remove Button Contents&#8221;.  Then add an arrow to the button and set its direction to down.  The button will be used to display a Calendar so that the people can select the due date.</li>
<li>Add a label in 3,0 and set its label to &#8220;Priority:</li>
<li>Add a Combo Box in 3,1 and set its name to &#8220;cmbPriority&#8221;.  Set its items to &#8220;High&#8221;, &#8220;Medium&#8221;, and &#8220;Low&#8221;.</li>
</ul>
</li>
</ul>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/pylan_01/pylan_01_02.png" alt="Python GTD pyGTK" border="0" /></p>
<p><strong>The Calendar Window</strong></p>
<p>This is a popup window that we will us to let the user select the due date for their tasks.</p>
<ul>
<li>Add another window, and call it &#8220;calendarWindow&#8221;.  Set its border width to 1, its title to &#8220;&#8221;, its type to &#8220;popup&#8221;, modal to &#8220;yes&#8221;, resizeable to &#8220;no&#8221;, and skip taskbar to &#8220;yes&#8221;.</li>
<li>Add a vertical box to the window with two rows.  </li>
<li>In the first row ad a calendar widget and call it &#8220;calendarWidget&#8221;</li>
<li>In the second row add a button with the name &#8220;btnNoDate&#8221;.  Set the label to be &#8220;No Date&#8221; and the icon to be gtk-no.  Add a clicked signal handler.</li>
</ul>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/pylan_01/pylan_01_03.png" alt="Python GTD pyGTK" border="0" /></p>
<h2><a name="todoTask"></a>The todo.Task object</h2>
<p>The todo.Task object is used to represent the task item in the same way that the todo.Category object represents a category item.  The astute among you will notice that I renamed the category object, I just thought that todo.Category looked so much better then todo.todoCategory.</p>
<p>Since we will be using the datetime and time python modules to represent the due date for tasks we need to import them in the todo file:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">import </span><span class="hl-identifier">datetime
</span><span class="hl-reserved">import </span><span class="hl-identifier">time</span></pre></div></div>
<p>The todo.Task class is relatively simple, if you&#8217;ve read the first tutorial it should be very easy to understand this code:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">class </span><span class="hl-identifier">Task</span><span class="hl-brackets">(</span><span class="hl-identifier">todoBase</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This is a task in the todoTree.  It represents
	something that needs to be done.
	</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-comment">#Name property
	</span><span class="hl-reserved">def </span><span class="hl-identifier">__get_name</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-reserved">return </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_name
	</span><span class="hl-reserved">def </span><span class="hl-identifier">__set_name</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">name</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_name</span><span class="hl-default"> = </span><span class="hl-identifier">name
	name</span><span class="hl-default"> = </span><span class="hl-builtin">property</span><span class="hl-brackets">(</span><span class="hl-identifier">__get_name</span><span class="hl-code">, </span><span class="hl-identifier">__set_name</span><span class="hl-brackets">)

	</span><span class="hl-comment">#Due date property
	</span><span class="hl-reserved">def </span><span class="hl-identifier">__get_due_date</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-reserved">return </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_due_date
	</span><span class="hl-reserved">def </span><span class="hl-identifier">__set_due_date</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">due_date</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_due_date</span><span class="hl-default"> = </span><span class="hl-identifier">due_date
	due_date</span><span class="hl-default"> = </span><span class="hl-builtin">property</span><span class="hl-brackets">(</span><span class="hl-identifier">__get_due_date</span><span class="hl-code">, </span><span class="hl-identifier">__set_due_date</span><span class="hl-brackets">)

	</span><span class="hl-comment">#Due time property
	</span><span class="hl-reserved">def </span><span class="hl-identifier">__get_due_time</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-reserved">return </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_due_time
	</span><span class="hl-reserved">def </span><span class="hl-identifier">__set_due_time</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">due_time</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_due_time</span><span class="hl-default"> = </span><span class="hl-identifier">due_time
	due_time</span><span class="hl-default"> = </span><span class="hl-builtin">property</span><span class="hl-brackets">(</span><span class="hl-identifier">__get_due_time</span><span class="hl-code">, </span><span class="hl-identifier">__set_due_time</span><span class="hl-brackets">)

	</span><span class="hl-comment">#priority property
	</span><span class="hl-reserved">def </span><span class="hl-identifier">__get_priority</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-reserved">return </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_priority
	</span><span class="hl-reserved">def </span><span class="hl-identifier">__set_priority</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">priority</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_priority</span><span class="hl-default"> = </span><span class="hl-identifier">priority
	priority</span><span class="hl-default"> = </span><span class="hl-builtin">property</span><span class="hl-brackets">(</span><span class="hl-identifier">__get_priority</span><span class="hl-code">, </span><span class="hl-identifier">__set_priority</span><span class="hl-brackets">)

	</span><span class="hl-reserved">def </span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">name</span><span class="hl-brackets">)</span><span class="hl-default">:

		</span><span class="hl-comment">#init variables
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_name</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;&quot;
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_due_date</span><span class="hl-default"> = </span><span class="hl-reserved">None
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_due_time</span><span class="hl-default"> = </span><span class="hl-reserved">None
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_priority</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;&quot;

		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">completed</span><span class="hl-default"> = </span><span class="hl-reserved">False
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">details</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;&quot;

		</span><span class="hl-comment">#set properties
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">name</span><span class="hl-default"> = </span><span class="hl-identifier">name
		</span><span class="hl-comment">#init base
		</span><span class="hl-identifier">todoBase</span><span class="hl-default">.</span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">TASK</span><span class="hl-brackets">)

		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Format strings so that the date and time
		formats returns can easily be changed.</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">date_format</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">%Y-%m-%d</span><span class="hl-quotes">&quot;
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">time_format</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">%I:%M%p</span><span class="hl-quotes">&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">__str__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-reserved">return </span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">todo .Task object: name = %s</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-default"> % </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">name</span><span class="hl-brackets">)

	</span><span class="hl-reserved">def </span><span class="hl-identifier">get_column_list</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">todoColumnList</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is used to get the display list
		for the todoTree. The todoCOlumnList controls the
		order that the list will be returned in.
		@param todoColumnList - list - A list of todoColumn items.
		Their type member should use used to determine the order
		of the returned list.
		@returns list - A list for the todoTree.
		</span><span class="hl-quotes">&quot;&quot;&quot;

		</span><span class="hl-identifier">lst_return</span><span class="hl-default"> = </span><span class="hl-brackets">[]
		</span><span class="hl-comment"># Loop through the columns and create the return list
		</span><span class="hl-reserved">for </span><span class="hl-identifier">item_column </span><span class="hl-reserved">in </span><span class="hl-identifier">todoColumnList</span><span class="hl-default">:
			</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-code"> == </span><span class="hl-identifier">pyLan</span><span class="hl-code">.</span><span class="hl-identifier">COL_OBJECT</span><span class="hl-brackets">)</span><span class="hl-default">:
				</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)
			</span><span class="hl-identifier">elif </span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-code"> == </span><span class="hl-identifier">pyLan</span><span class="hl-code">.</span><span class="hl-identifier">COL_OBJECT_TYPE</span><span class="hl-brackets">)</span><span class="hl-default">:
				</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">type</span><span class="hl-brackets">)
			</span><span class="hl-identifier">elif </span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-code"> == </span><span class="hl-identifier">pyLan</span><span class="hl-code">.</span><span class="hl-identifier">COL_TITLE</span><span class="hl-brackets">)</span><span class="hl-default">:
				</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">name</span><span class="hl-brackets">)
			</span><span class="hl-identifier">elif </span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-code"> == </span><span class="hl-identifier">pyLan</span><span class="hl-code">.</span><span class="hl-identifier">COL_DETAILS</span><span class="hl-brackets">)</span><span class="hl-default">:
				</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">details</span><span class="hl-brackets">)
			</span><span class="hl-identifier">elif </span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-code"> == </span><span class="hl-identifier">pyLan</span><span class="hl-code">.</span><span class="hl-identifier">COL_DUE</span><span class="hl-brackets">)</span><span class="hl-default">:
				</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">get_datetime_string</span><span class="hl-brackets">())
			</span><span class="hl-identifier">elif </span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-code"> == </span><span class="hl-identifier">pyLan</span><span class="hl-code">.</span><span class="hl-identifier">COL_PRIORITY</span><span class="hl-brackets">)</span><span class="hl-default">:
				</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">priority</span><span class="hl-brackets">)
			</span><span class="hl-identifier">elif </span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-code"> == </span><span class="hl-identifier">pyLan</span><span class="hl-code">.</span><span class="hl-identifier">COL_COMPLETED</span><span class="hl-brackets">)</span><span class="hl-default">:
				</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">completed</span><span class="hl-brackets">)</span><span class="hl-default">:
					</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">True</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">))
				</span><span class="hl-reserved">else</span><span class="hl-default">:
					</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">False</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">))
			</span><span class="hl-reserved">else</span><span class="hl-default">:
				</span><span class="hl-identifier">helper</span><span class="hl-default">.</span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Error unknown column ID: %d</span><span class="hl-quotes">&quot;</span><span class="hl-code"> % </span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-brackets">))
				</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)

		</span><span class="hl-reserved">return </span><span class="hl-identifier">lst_return

	</span><span class="hl-reserved">def </span><span class="hl-identifier">get_date_string</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is used to get the Date string. It will
		be returned in the format of self.date_format.
		@returns - string - The current Due date or &quot;&quot; if nothing
		has been set yet.
		</span><span class="hl-quotes">&quot;&quot;&quot;

		</span><span class="hl-identifier">date_string</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;&quot;
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">due_date</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-identifier">date_string</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">due_date</span><span class="hl-default">.</span><span class="hl-identifier">strftime</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">date_format</span><span class="hl-brackets">)
		</span><span class="hl-reserved">return </span><span class="hl-identifier">date_string

	</span><span class="hl-reserved">def </span><span class="hl-identifier">get_time_string</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is used to get the time string. It will
		be returned in the format of self.time_format.
		@returns - string - The current Due time or &quot;&quot; if nothing
		has been set yet.
		</span><span class="hl-quotes">&quot;&quot;&quot;

		</span><span class="hl-identifier">date_string</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;&quot;
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">due_time</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-identifier">date_string</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">due_time</span><span class="hl-default">.</span><span class="hl-identifier">strftime</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">time_format</span><span class="hl-brackets">)
		</span><span class="hl-reserved">return </span><span class="hl-identifier">date_string

	</span><span class="hl-reserved">def </span><span class="hl-identifier">get_datetime_string</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Used to get the date and the time in their
		specified formats.
		@returns - string - the date and the time.
		</span><span class="hl-quotes">&quot;&quot;&quot;

		</span><span class="hl-identifier">date_string</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">get_date_string</span><span class="hl-brackets">()
		</span><span class="hl-identifier">time_string</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">get_time_string</span><span class="hl-brackets">()
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-builtin">len</span><span class="hl-brackets">(</span><span class="hl-identifier">time_string</span><span class="hl-brackets">)</span><span class="hl-code">&gt;</span><span class="hl-number">0</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-identifier">date_string</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">%s %s</span><span class="hl-quotes">&quot;</span><span class="hl-default"> % </span><span class="hl-brackets">(</span><span class="hl-identifier">date_string</span><span class="hl-code">, </span><span class="hl-identifier">time_string</span><span class="hl-brackets">)

		</span><span class="hl-reserved">return </span><span class="hl-identifier">date_string</span></pre></div></div>
<p>One thing that you may notice is how we use the datetime and time modules.  You should notice that we have two member variables: </p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">date_format</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">%Y-%m-%d</span><span class="hl-quotes">&quot;
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">time_format</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">%I:%M%p</span><span class="hl-quotes">&quot;</span></pre></div></div>
<p>These variable store the date and time format that we will use to display the date and time in the tree.  We will pass the format strings to the<a href="http://docs.python.org/lib/strftime-behavior.html#strftime-behavior">strftime</a> function (from the python docs):</p>
<blockquote><p>
date, datetime, and time objects all support a strftime(format) method, to create a string representing the time under the control of an explicit format string. Broadly speaking, d.strftime(fmt) acts like the time module&#8217;s time.strftime(fmt, d.timetuple()) although not all objects support a timetuple() method.
</p></blockquote>
<p>We define them as members of the class so that they can be changed easily and even pickled with the object itself.</p>
<p>I won&#8217;t go into too much other detail about this class since it&#8217;s pretty much just a data holder for now, and is very similar to the todo.Category object explained in tutorial one except that it has a few more data members.</p>
<h2><a name="addedittodoTask"></a>Adding a todo.Task object</h2>
<p>For this we are going to have to use the task dialog that we created in the GUI stage.  We are going to encapsulate the handling of displaying the dialog in a class.  It made sense to me to do this since this dialog is far more complicated then many that we have used before.</p>
<p>The class is called TaskDialog and I created it in the todo.py file:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">class </span><span class="hl-identifier">TaskDialog</span><span class="hl-brackets">(</span><span class="hl-identifier">object</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This is a class that is used to show a taskDialog.  It
	can be used to create or edit a todo.Task.  To create one
	simply initialize the class and do not pass a totoTask.  If you
	want to edit an object initialize with the object that
	you want to edit.</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">glade_file</span><span class="hl-code">, </span><span class="hl-identifier">task</span><span class="hl-code"> = </span><span class="hl-reserved">None</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Initialize the task dialog.
		@param glade_file - string - the glade file for this
		dialog.
		@param task - todo.Task - None to create a new
		todo.Task object, or the object that you wish to
		edit.
		</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">glade_file</span><span class="hl-default"> = </span><span class="hl-identifier">glade_file
		self</span><span class="hl-default">.</span><span class="hl-identifier">task</span><span class="hl-default"> = </span><span class="hl-identifier">task

		</span><span class="hl-comment">#Get the widget tree
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">glade</span><span class="hl-default">.</span><span class="hl-identifier">XML</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">glade_file</span><span class="hl-code">, </span><span class="hl-quotes">&quot;</span><span class="hl-string">taskDialog</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-comment">#Connect with yourself
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">signal_autoconnect</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">dialog</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">taskDialog</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-comment">#get the widgets from the dlg
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">en_title</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">enTitle</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">tv_details</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">tvDetails</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">en_due</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">enDue</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">btn_due</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">btnDue</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">cmb_Time</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">cmbTime</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">cmb_priority</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">cmbPriority</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">check_completed</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">checkCompleted</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-comment">#select the items in the combo boxes
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">cmb_Time</span><span class="hl-default">.</span><span class="hl-identifier">set_active</span><span class="hl-brackets">(</span><span class="hl-number">0</span><span class="hl-brackets">)
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">cmb_priority</span><span class="hl-default">.</span><span class="hl-identifier">set_active</span><span class="hl-brackets">(</span><span class="hl-number">2</span><span class="hl-brackets">)

		</span><span class="hl-identifier">wTreeDue</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">glade</span><span class="hl-default">.</span><span class="hl-identifier">XML</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">glade_file</span><span class="hl-code">, </span><span class="hl-quotes">&quot;</span><span class="hl-string">calendarWindow</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">calendar_window</span><span class="hl-default"> = </span><span class="hl-identifier">wTreeDue</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">calendarWindow</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-identifier">wTreeDue</span><span class="hl-default">.</span><span class="hl-identifier">signal_autoconnect</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)

		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">update_dialog_from_object</span><span class="hl-brackets">()</span></pre></div></div>
<p>As you can see what happens here is the TaskDialog is initialized with the path to the glade file that contains the taskDialog, and an optional todo.Task object.  If the todo.Task is not specified (or None) when creating the TaskDialog then the TaskDialog instance is in &#8220;add&#8221; mode, which means that it will create a todo.Task object with the settings specified on the dialog if the user presses the Ok button.</p>
<p>If an instance of a todo.Task object is used when constructing the TaskDialog then the dialog is in &#8220;edit&#8221; mode, and it will modify the object that was passed if the user presses the Ok button.</p>
<p>So not much happens in the __init__ function except we load the dialog and the calendar window from the glade file (the calendar window must be set not to be initially visible in the glade file!  Otherwise it will be shown when it is loaded.) and we auto connect the signals from the dialog and the window with the TaskDialog class.</p>
<p>Then we get access to all of the important widgets on the task dialog.  You&#8217;ll also notice that we set the active item in both the time and priority gtk.ComboBox&#8217;s using the <a href="http://www.pygtk.org/docs/pygtk/class-gtkcombobox.html#method-gtkcombobox--set-active">set_active()</a> function. </p>
<p>We do this so that the first item is selected in the time gtk.ComboBox (the blank)  and we select the third item in the priority gtk.ComboBox so that the low priority item is selected.</p>
<p>At the end of the function we call the update_dialog_from_object() function, this function is used to update what is displayed on the dialog based on the contents of the todo.Task.  There is a sister function named save_data_to_object() that is used to update the todo.Task item with the contents of the dialog:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">update_dialog_from_object</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Used to update the settings on the dialog</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">task</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-comment">#Title
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">en_title</span><span class="hl-default">.</span><span class="hl-identifier">set_text</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">task</span><span class="hl-code">.</span><span class="hl-identifier">name</span><span class="hl-brackets">)
		</span><span class="hl-comment">#Details
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">set_details</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">task</span><span class="hl-code">.</span><span class="hl-identifier">details</span><span class="hl-brackets">)
		</span><span class="hl-comment">#Due Date
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">en_due</span><span class="hl-default">.</span><span class="hl-identifier">set_text</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">task</span><span class="hl-code">.</span><span class="hl-identifier">get_date_string</span><span class="hl-brackets">())
		</span><span class="hl-comment">#Due Time
		</span><span class="hl-identifier">found_iter</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">find_text_in_combo</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">cmb_Time</span><span class="hl-code">
						, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">task</span><span class="hl-code">.</span><span class="hl-identifier">get_time_string</span><span class="hl-brackets">())
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">found_iter</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">cmb_Time</span><span class="hl-default">.</span><span class="hl-identifier">set_active_iter</span><span class="hl-brackets">(</span><span class="hl-identifier">found_iter</span><span class="hl-brackets">)
		</span><span class="hl-comment">#priority
		</span><span class="hl-identifier">found_iter</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">find_text_in_combo</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">cmb_priority</span><span class="hl-code">
									, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">task</span><span class="hl-code">.</span><span class="hl-identifier">priority</span><span class="hl-brackets">)
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">found_iter</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">cmb_priority</span><span class="hl-default">.</span><span class="hl-identifier">set_active_iter</span><span class="hl-brackets">(</span><span class="hl-identifier">found_iter</span><span class="hl-brackets">)
		</span><span class="hl-comment">#completed
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">check_completed</span><span class="hl-default">.</span><span class="hl-identifier">set_active</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">task</span><span class="hl-code">.</span><span class="hl-identifier">completed</span><span class="hl-brackets">)


</span><span class="hl-reserved">def </span><span class="hl-identifier">save_data_to_object</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is used to read the data from the dialog
	and then store it in the todo.Task object.
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">task</span><span class="hl-code"> == </span><span class="hl-reserved">None</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">task</span><span class="hl-default"> = </span><span class="hl-identifier">Task</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">en_title</span><span class="hl-code">.</span><span class="hl-identifier">get_text</span><span class="hl-brackets">())
	</span><span class="hl-reserved">else</span><span class="hl-default">:
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">task</span><span class="hl-default">.</span><span class="hl-identifier">name</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">en_title</span><span class="hl-default">.</span><span class="hl-identifier">get_text</span><span class="hl-brackets">()

	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">task</span><span class="hl-default">.</span><span class="hl-identifier">details</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">get_details</span><span class="hl-brackets">()

	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">task</span><span class="hl-default">.</span><span class="hl-identifier">set_due_date_from_string</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">en_due</span><span class="hl-code">.</span><span class="hl-identifier">get_text</span><span class="hl-brackets">())
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">task</span><span class="hl-default">.</span><span class="hl-identifier">set_due_time_from_string</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">cmb_Time</span><span class="hl-code">.</span><span class="hl-identifier">get_active_text</span><span class="hl-brackets">())
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">task</span><span class="hl-default">.</span><span class="hl-identifier">priority</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">cmb_priority</span><span class="hl-default">.</span><span class="hl-identifier">get_active_text</span><span class="hl-brackets">()
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">task</span><span class="hl-default">.</span><span class="hl-identifier">completed</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">check_completed</span><span class="hl-default">.</span><span class="hl-identifier">get_active</span><span class="hl-brackets">()</span></pre></div></div>
<p>Over all these two functions are pretty straightforward, they simply marshal the date between the dialog and the todo.Task.  You&#8217;ll see that there are some helper functions that are used in both functions in order to make saving and loading the data from the object to the dialog easier.  For example to set the text in the gtk.TextView we use the set_details() function, and then to get the text we use the get_details() function:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">get_details</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function gets the details from the TextView
	@returns string - The text in the gtk.TextView
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">txtBuffer</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">tv_details</span><span class="hl-default">.</span><span class="hl-identifier">get_buffer</span><span class="hl-brackets">()
	</span><span class="hl-reserved">return </span><span class="hl-identifier">txtBuffer</span><span class="hl-default">.</span><span class="hl-identifier">get_text</span><span class="hl-brackets">(</span><span class="hl-code">*</span><span class="hl-identifier">txtBuffer</span><span class="hl-code">.</span><span class="hl-identifier">get_bounds</span><span class="hl-brackets">())

</span><span class="hl-reserved">def </span><span class="hl-identifier">set_details</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">details</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function sets the text in the defails gtk.TextView
	@param details - string - The text that will be
	put into the gtk.TextView.
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">txtBuffer</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">tv_details</span><span class="hl-default">.</span><span class="hl-identifier">get_buffer</span><span class="hl-brackets">()
	</span><span class="hl-identifier">txtBuffer</span><span class="hl-default">.</span><span class="hl-identifier">set_text</span><span class="hl-brackets">(</span><span class="hl-identifier">details</span><span class="hl-brackets">)</span></pre></div></div>
<p>These two function are very simple, we get the <a href="http://www.pygtk.org/docs/pygtk/class-gtktextbuffer.html">gkt.TextBuffer</a> associated with the gtk.TextView and then we use the <a href="http://www.pygtk.org/docs/pygtk/class-gtktextbuffer.html#method-gtktextbuffer--get-text">get_text()</a> and <a href="http://www.pygtk.org/docs/pygtk/class-gtktextbuffer.html#method-gtktextbuffer--set-text">set_text()</a> functions to get and set the text.</p>
<p>The next helper that we use is the find_text_in_combo() function:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">find_text_in_combo</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">combobox</span><span class="hl-code">, </span><span class="hl-identifier">text</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This is a helper function use to find text in
	a gtk.ComboBox.
	@param conbobox gtk.ComboBox - This should contain
	text.
	@param text - string - the text that we are looking
	to find.
	@returns - gtk.TreeIter - The iter at the found
	position or None if nothing was found.
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">found_iter</span><span class="hl-default"> = </span><span class="hl-reserved">None </span><span class="hl-comment">#The Iter where text is found
	#Get the gtk.TreeModel associated with the combo
	</span><span class="hl-identifier">combo_model</span><span class="hl-default"> = </span><span class="hl-identifier">combobox</span><span class="hl-default">.</span><span class="hl-identifier">get_model</span><span class="hl-brackets">()
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">combo_model</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-comment">#Get the first iter in the model
		</span><span class="hl-identifier">search_iter</span><span class="hl-default"> = </span><span class="hl-identifier">combo_model</span><span class="hl-default">.</span><span class="hl-identifier">get_iter_first</span><span class="hl-brackets">()
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Now loop through the model checking for
		matches until one is found.  Or until
		we have ran out of iters.</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">while </span><span class="hl-brackets">((</span><span class="hl-identifier">found_iter</span><span class="hl-code"> == </span><span class="hl-reserved">None</span><span class="hl-brackets">)
			</span><span class="hl-identifier">and </span><span class="hl-brackets">(</span><span class="hl-identifier">search_iter</span><span class="hl-brackets">))</span><span class="hl-default">:
			</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">text</span><span class="hl-code"> == </span><span class="hl-identifier">combo_model</span><span class="hl-brackets">[</span><span class="hl-identifier">search_iter</span><span class="hl-brackets">][</span><span class="hl-number">0</span><span class="hl-brackets">])</span><span class="hl-default">:
				</span><span class="hl-comment">#Found!
				</span><span class="hl-identifier">found_iter</span><span class="hl-default"> = </span><span class="hl-identifier">search_iter
			</span><span class="hl-reserved">else</span><span class="hl-default">:
				</span><span class="hl-identifier">search_iter</span><span class="hl-default"> = </span><span class="hl-identifier">combo_model</span><span class="hl-default">.</span><span class="hl-identifier">iter_next</span><span class="hl-brackets">(</span><span class="hl-identifier">search_iter</span><span class="hl-brackets">)
	</span><span class="hl-reserved">return </span><span class="hl-identifier">found_iter</span></pre></div></div>
<p>This function takes a gtk.ComboBox and a string as parameters and searches the gtk.TreeModel associated with the gtk.ComboBox for the text.  If the text is found then the gtk.TreeIter associated with that item is returned, otherwise None is returned.  The function is pretty simple, but I was slightly disappointed that there was no built in way to do this&#8230;perhaps I missed something or perhaps this will be addressed in a future release.</p>
<p>Then once we have the gtk.TreeIter we can use the <a href="http://www.pygtk.org/docs/pygtk/class-gtkcombobox.html#method-gtkcombobox--set-active-iter">set_active_iter()</a> to set that item as the current item in the gtk.ComboBox.  </p>
<p>To get the date and time we use the todo.Task.get_time_string() and todo.Task.get_date_string() functions that we have already touched on. The setting of the due date and due time however is slightly more complicated and I have not gone over it yet.  What we need to do is generate datetime.date and datetime.time instances from strings.</p>
<p>The following two functions in the todo.Task are used to accomplish this:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">set_due_date_from_string</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">date_string</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is used to set the due_date based
	on a string. We will try to parse the string.  We will try
	to parse the following formats: YYYY-MM-DD
	, MM-DD-YYYY, or DD-MM-YYYY
	@param date_string - string - The string that we will
	turn into our datetime.date object.
	@returns boolean - success or failure</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">success</span><span class="hl-default"> = </span><span class="hl-reserved">False
	</span><span class="hl-comment">#Switch chars
	</span><span class="hl-identifier">date_string</span><span class="hl-default"> = </span><span class="hl-identifier">date_string</span><span class="hl-default">.</span><span class="hl-identifier">replace</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">/</span><span class="hl-quotes">&quot;</span><span class="hl-code">,</span><span class="hl-quotes">&quot;</span><span class="hl-string">-</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">date_string</span><span class="hl-default"> = </span><span class="hl-identifier">date_string</span><span class="hl-default">.</span><span class="hl-identifier">replace</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-special">\\</span><span class="hl-quotes">&quot;</span><span class="hl-code">,</span><span class="hl-quotes">&quot;</span><span class="hl-string">-</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">date_string</span><span class="hl-default"> = </span><span class="hl-identifier">date_string</span><span class="hl-default">.</span><span class="hl-identifier">replace</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot; &quot;</span><span class="hl-code">,</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)
	</span><span class="hl-comment">#All the possible formats
	</span><span class="hl-identifier">lst_formats</span><span class="hl-default"> = </span><span class="hl-brackets">[
		</span><span class="hl-quotes">&quot;</span><span class="hl-string">%Y-%m-%d</span><span class="hl-quotes">&quot;</span><span class="hl-code">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">%m-%d-%Y</span><span class="hl-quotes">&quot;</span><span class="hl-code">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">%d-%m-%Y</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">]
	</span><span class="hl-comment">#Now loop through the formats and try to find a match
	</span><span class="hl-identifier">date</span><span class="hl-default"> = </span><span class="hl-reserved">None
	for </span><span class="hl-identifier">format </span><span class="hl-reserved">in </span><span class="hl-identifier">lst_formats</span><span class="hl-default">:
		</span><span class="hl-reserved">try</span><span class="hl-default">:
			</span><span class="hl-identifier">date</span><span class="hl-default"> = </span><span class="hl-identifier">datetime</span><span class="hl-default">.</span><span class="hl-identifier">datetime</span><span class="hl-brackets">(</span><span class="hl-code">*</span><span class="hl-identifier">time</span><span class="hl-code">.</span><span class="hl-identifier">strptime</span><span class="hl-brackets">(</span><span class="hl-identifier">date_string</span><span class="hl-code">
						, </span><span class="hl-identifier">format</span><span class="hl-brackets">)[</span><span class="hl-number">0</span><span class="hl-code">:</span><span class="hl-number">3</span><span class="hl-brackets">])
			</span><span class="hl-comment">#We found a matching format
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">due_date</span><span class="hl-default"> = </span><span class="hl-identifier">date
			</span><span class="hl-comment">#Save the format
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">date_format</span><span class="hl-default"> = </span><span class="hl-identifier">format
			</span><span class="hl-reserved">break
		except</span><span class="hl-default">:
			</span><span class="hl-comment">#failed
			</span><span class="hl-reserved">pass
	return </span><span class="hl-identifier">success

</span><span class="hl-reserved">def </span><span class="hl-identifier">set_due_time_from_string</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">time_string</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is used to set the due_time based
	on a string. We will try to parse the string.  We will try
	to parse some formats.
	@param time_string - string - A string representing the
	time.
	@returns - boolean success or failer
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">success</span><span class="hl-default"> = </span><span class="hl-reserved">False
	</span><span class="hl-comment">#strip whitespace
	</span><span class="hl-identifier">time_string</span><span class="hl-default"> = </span><span class="hl-identifier">time_string</span><span class="hl-default">.</span><span class="hl-identifier">replace</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot; &quot;</span><span class="hl-code">,</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)
	</span><span class="hl-comment">#All the possible formats
	</span><span class="hl-identifier">lst_formats</span><span class="hl-default"> = </span><span class="hl-brackets">[
		</span><span class="hl-quotes">&quot;</span><span class="hl-string">%I:%M%p</span><span class="hl-quotes">&quot;</span><span class="hl-code">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">%H:%M%p</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">]
	</span><span class="hl-comment">#Now loop through the formats and try to find a match
	</span><span class="hl-identifier">due_time</span><span class="hl-default"> = </span><span class="hl-reserved">None
	for </span><span class="hl-identifier">format </span><span class="hl-reserved">in </span><span class="hl-identifier">lst_formats</span><span class="hl-default">:
		</span><span class="hl-reserved">try</span><span class="hl-default">:
			</span><span class="hl-identifier">time_struct</span><span class="hl-default"> = </span><span class="hl-identifier">time</span><span class="hl-default">.</span><span class="hl-identifier">strptime</span><span class="hl-brackets">(</span><span class="hl-identifier">time_string</span><span class="hl-code">
						, </span><span class="hl-identifier">format</span><span class="hl-brackets">)
			</span><span class="hl-identifier">due_time</span><span class="hl-default"> = </span><span class="hl-identifier">datetime</span><span class="hl-default">.</span><span class="hl-identifier">time</span><span class="hl-brackets">(</span><span class="hl-identifier">time_struct</span><span class="hl-code">.</span><span class="hl-identifier">tm_hour</span><span class="hl-code">
						, </span><span class="hl-identifier">time_struct</span><span class="hl-code">.</span><span class="hl-identifier">tm_min</span><span class="hl-brackets">)
			</span><span class="hl-comment">#Save the time
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">due_time</span><span class="hl-default"> = </span><span class="hl-identifier">due_time
			</span><span class="hl-comment">#Save the format that was used
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">time_format</span><span class="hl-default"> = </span><span class="hl-identifier">format
			success</span><span class="hl-default"> = </span><span class="hl-reserved">True
			break
		except</span><span class="hl-default">:
			</span><span class="hl-comment">#failed
			</span><span class="hl-reserved">pass
	return </span><span class="hl-identifier">success</span></pre></div></div>
<p>The functions are slightly difficult to understand I will take a little bit of time to explain how they work.  The general idea of the functions is to loop through a list of possible formats and try to create either a datetime.date or datetime.time instance using the current format.</p>
<p>If the creation throws an exception then it has failed, if it does not then it has succeeded and we need to save the format that we used (it becomes the format that we display things in, so if you type in the date in a certain way it will be displayed in that way,) and save the actual instance.</p>
<p>We use the time modules strptime function to create a struct_time, which we then use to create the date or time instance (from the <a href="http://docs.python.org/lib/module-time.html">python documentation</a>):</p>
<blockquote><p>
Parse a string representing a time according to a format. The return value is a struct_time as returned by gmtime() or localtime(). The format parameter uses the same directives as those used by strftime(); it defaults to &#8220;%a %b %d %H:%M:%S %Y&#8221; which matches the formatting returned by ctime(). If string cannot be parsed according to format, ValueError is raised. If the string to be parsed has excess data after parsing, ValueError is raised. The default values used to fill in any missing data when more accurate values cannot be inferred are (1900, 1, 1, 0, 0, 0, 0, 1, -1) .</p>
<p>Support for the %Z directive is based on the values contained in tzname and whether daylight is true. Because of this, it is platform-specific except for recognizing UTC and GMT which are always known (and are considered to be non-daylight savings timezones).
</p></blockquote>
<p>struct_time is simply a sequence of 9 integers that we can then use to create our date or time instances.  The only real difference between the two is that instead of always being able to use the first three numbers in the struct_time sequence when creating the date instance we need to reference the specific time members when creating the time object.</p>
<p>So the code is a bit complicated, but when you look the overview of what it&#8217;s doing it&#8217;s actually pretty simple.</p>
<p>The next thing we have to do is actually show do the dialog:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">run</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Show the dialog</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-identifier">break_out</span><span class="hl-default"> = </span><span class="hl-reserved">False
	</span><span class="hl-identifier">while </span><span class="hl-brackets">(</span><span class="hl-reserved">not </span><span class="hl-identifier">break_out</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">result</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">dialog</span><span class="hl-default">.</span><span class="hl-identifier">run</span><span class="hl-brackets">()
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">result</span><span class="hl-code">==</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_OK</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Save the date to the object becuase the use pressed
			ok.</span><span class="hl-quotes">&quot;&quot;&quot;
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">save_data_to_object</span><span class="hl-brackets">()
			</span><span class="hl-identifier">break_out</span><span class="hl-default"> = </span><span class="hl-reserved">True
			</span><span class="hl-comment">#Validate here eventually
		</span><span class="hl-reserved">else</span><span class="hl-default">:
			</span><span class="hl-identifier">break_out</span><span class="hl-default"> = </span><span class="hl-reserved">True

	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">dialog</span><span class="hl-default">.</span><span class="hl-identifier">hide</span><span class="hl-brackets">()

	</span><span class="hl-reserved">return </span><span class="hl-identifier">result</span><span class="hl-default">;</span></pre></div></div>
<p>Another simple function (I seem to say that a lot don&#8217;t I?) we&#8217;re just showing the dialog, and if the users clicks on the OK button we save the data to our todo.Task object and then we leave.  You might wonder why this is done in a loop, the reason is so that in the future we will be able to validate what the user has entered.</p>
<p>So when if the user clicks on the OK button and the validation fails, then we would not set break_out to True, and the dialog would be shown again.  This is just an easy way to set up the dialog for future validation.</p>
<p>The last thing that we need to do is actually use the dialog!  We do this in the on_add_task() function in our main PyLan class:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_add_task</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when we want to add a task.</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-identifier">model</span><span class="hl-default">, </span><span class="hl-identifier">selection_iter</span><span class="hl-default">, </span><span class="hl-identifier">todo_cat</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">get_category_selected</span><span class="hl-brackets">()
	</span><span class="hl-comment">#Make sure that a category is selected
	</span><span class="hl-identifier">if </span><span class="hl-brackets">((</span><span class="hl-identifier">model</span><span class="hl-brackets">) </span><span class="hl-identifier">and </span><span class="hl-brackets">(</span><span class="hl-identifier">selection_iter</span><span class="hl-brackets">) </span><span class="hl-identifier">and </span><span class="hl-brackets">(</span><span class="hl-identifier">todo_cat</span><span class="hl-brackets">))</span><span class="hl-default">:
		</span><span class="hl-identifier">task_dialog</span><span class="hl-default"> = </span><span class="hl-identifier">todo</span><span class="hl-default">.</span><span class="hl-identifier">TaskDialog</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">gladefile</span><span class="hl-brackets">)
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">task_dialog</span><span class="hl-code">.</span><span class="hl-identifier">run</span><span class="hl-brackets">()</span><span class="hl-code"> == </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_OK</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-comment">#Append to the tree
			</span><span class="hl-identifier">task_dialog</span><span class="hl-default">.</span><span class="hl-identifier">task</span><span class="hl-default">.</span><span class="hl-identifier">add_to_tree</span><span class="hl-brackets">(</span><span class="hl-identifier">model</span><span class="hl-code">
				, </span><span class="hl-identifier">selection_iter</span><span class="hl-code">
				, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">__tree_columns</span><span class="hl-brackets">)
			</span><span class="hl-comment">#Add to the Category
			</span><span class="hl-identifier">todo_cat</span><span class="hl-default">.</span><span class="hl-identifier">add_child</span><span class="hl-brackets">(</span><span class="hl-identifier">task_dialog</span><span class="hl-code">.</span><span class="hl-identifier">task</span><span class="hl-brackets">)</span></pre></div></div>
<p>Pretty simple stuff here again (I&#8217;m like a broken record!) we get the selected category, if all of the variables come through then we create the todo.TaskDialog in &#8220;add&#8221; mode and then we run it.  If the response is gtk.RESPONSE_OK we add the task to the tree, and then add the task to the category.</p>
<p>If you are wondering about the last two steps there, then you should check out the first tutorial where those helpers are explained.</p>
<h2><a name="calendarWindow"></a>Showing the Calendar window</h2>
<p>This next step took me a long time to figure out even though the actual code is very short.  This step is getting the calendar window, that we created in our glade project, to display when the user clicks on the Due Date &#8220;dropdown&#8221; button.</p>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/pylan_01/pylan_01_07.png" alt="Python GTD pyGTK" border="0" /></p>
<p>So here is the code:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_btnDue_clicked</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the due button is clicked.</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">rect</span><span class="hl-default"> = </span><span class="hl-identifier">widget</span><span class="hl-default">.</span><span class="hl-identifier">get_allocation</span><span class="hl-brackets">()
	</span><span class="hl-identifier">x</span><span class="hl-default">, </span><span class="hl-identifier">y</span><span class="hl-default"> = </span><span class="hl-identifier">widget</span><span class="hl-default">.</span><span class="hl-identifier">window</span><span class="hl-default">.</span><span class="hl-identifier">get_origin</span><span class="hl-brackets">()
	</span><span class="hl-identifier">cal_width</span><span class="hl-default">, </span><span class="hl-identifier">cal_height</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">calendar_window</span><span class="hl-default">.</span><span class="hl-identifier">get_size</span><span class="hl-brackets">()

	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">calendar_window</span><span class="hl-default">.</span><span class="hl-identifier">move</span><span class="hl-brackets">((</span><span class="hl-identifier">x</span><span class="hl-code"> + </span><span class="hl-identifier">rect</span><span class="hl-code">.</span><span class="hl-identifier">x</span><span class="hl-code"> - </span><span class="hl-identifier">cal_width</span><span class="hl-code"> + </span><span class="hl-identifier">rect</span><span class="hl-code">.</span><span class="hl-identifier">width</span><span class="hl-brackets">)</span><span class="hl-code">
		, </span><span class="hl-brackets">(</span><span class="hl-identifier">y</span><span class="hl-code"> + </span><span class="hl-identifier">rect</span><span class="hl-code">.</span><span class="hl-identifier">y</span><span class="hl-code"> + </span><span class="hl-identifier">rect</span><span class="hl-code">.</span><span class="hl-identifier">height</span><span class="hl-brackets">))

	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">calendar_window</span><span class="hl-default">.</span><span class="hl-identifier">show</span><span class="hl-brackets">()

	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Because some window managers ignore move before
	you show a window.</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">calendar_window</span><span class="hl-default">.</span><span class="hl-identifier">move</span><span class="hl-brackets">((</span><span class="hl-identifier">x</span><span class="hl-code"> + </span><span class="hl-identifier">rect</span><span class="hl-code">.</span><span class="hl-identifier">x</span><span class="hl-code"> - </span><span class="hl-identifier">cal_width</span><span class="hl-code"> + </span><span class="hl-identifier">rect</span><span class="hl-code">.</span><span class="hl-identifier">width</span><span class="hl-brackets">)</span><span class="hl-code">
		, </span><span class="hl-brackets">(</span><span class="hl-identifier">y</span><span class="hl-code"> + </span><span class="hl-identifier">rect</span><span class="hl-code">.</span><span class="hl-identifier">y</span><span class="hl-code"> + </span><span class="hl-identifier">rect</span><span class="hl-code">.</span><span class="hl-identifier">height</span><span class="hl-brackets">))</span></pre></div></div>
<p>So the first thing we do is use the <a href="http://www.pygtk.org/docs/pygtk/class-gtkwidget.html#method-gtkwidget--get-allocation">gtk.Widget.get_allocation()</a> function to get the <a href="http://www.pygtk.org/docs/pygtk/class-gdkrectangle.html">gtk.gdk.Rectangle</a> representing the size of the btnDue button.</p>
<p>Then we get the X and Y screen coordinates of the <a href="http://www.pygtk.org/docs/pygtk/class-gdkwindow.html">gtk.gdk.Window</a> (the window where the widget is actually drawn) using the <a href="http://www.pygtk.org/docs/pygtk/class-gdkwindow.html#method-gdkwindow--get-origin">get_origin()</a> function.  This is in root window coordinates instead of being relative to the parent window.  So this is actually the top left corner of the TaskDialog.</p>
<p>Then we get the height and width of the calendar window, and finally move it into position so that the top right corner of the calendar window lines up with the bottom right corner of the btnDue widget.</p>
<p>So, we take the top left corner of the dialog, and then add onto it the x,y coordinates of the btnDue (which are relative to the top left cornet of the TaskDialog) and then we add to it the height and the width of the btnDue so that we get the X and Y of the bottom right corder of the btnDue.  Then we subtract the width of the calendar window, so that the right side of the calendar window lines up with the right side of the btnDue.</p>
<p>If this seems a bit confusing try playing with the numbers a little bit, commenting out parts of the equation, and then see where the calendar window shows up.  After playing with it a little bit it should be pretty straight forward.</p>
<p>So what happens when then calendar window is displayed?  Well it will stay visible until the user double-clicks on a date, or clicks on the &#8220;No Date&#8221; button.</p>
<p>So we need to add two signal handlers:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_btnNoDate_clicked</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the now date widget is
	clicked.  This means that they do not want
	a due date.</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-comment">#Hide the calendar window
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">calendar_window</span><span class="hl-default">.</span><span class="hl-identifier">hide</span><span class="hl-brackets">()
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">en_due</span><span class="hl-default">.</span><span class="hl-identifier">set_text</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)

</span><span class="hl-reserved">def </span><span class="hl-identifier">on_calendarWidget_day_selected_double_click</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the used has double-clicked on a date
	in the calendar widget</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-comment">#Hide the calendar window
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">calendar_window</span><span class="hl-default">.</span><span class="hl-identifier">hide</span><span class="hl-brackets">()

	</span><span class="hl-identifier">year</span><span class="hl-default">, </span><span class="hl-identifier">month</span><span class="hl-default">, </span><span class="hl-identifier">day</span><span class="hl-default"> = </span><span class="hl-identifier">widget</span><span class="hl-default">.</span><span class="hl-identifier">get_date</span><span class="hl-brackets">()
	</span><span class="hl-identifier">month</span><span class="hl-default"> +=</span><span class="hl-number">1 </span><span class="hl-comment">#since it's 0 based
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">en_due</span><span class="hl-default">.</span><span class="hl-identifier">set_text</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">%d-%02d-%02d</span><span class="hl-quotes">&quot;</span><span class="hl-code"> % </span><span class="hl-brackets">(</span><span class="hl-identifier">year</span><span class="hl-code">, </span><span class="hl-identifier">month</span><span class="hl-code">, </span><span class="hl-identifier">day</span><span class="hl-brackets">))</span></pre></div></div>
<p>Pretty simple stuff here, if the &#8220;No Date&#8221; button is clicked then we blank out the due date edit field, and if a date is double clicked on, we get the date from the gtk.Calendar using the <a href="http://www.pygtk.org/docs/pygtk/class-gtkcalendar.html#method-gtkcalendar--get-date">get_date()</a> function.  Then we set the date in the edit fields based on those values.</p>
<p>For now we use a standard format, but in the future we could easily work with the date_format stored in the todo.Task object.</p>
<h2><a name="EditingItems"></a>Editing todo.Category and todo.Task items</h2>
<p>The next step is letting the user edit a todo.Category or todo.Task item that they have already added to the list.  Fortunately the way that we have shown both the Category and Task dialogs makes this step very easy.</p>
<p>The first thing that we need to do is add an &#8220;edit&#8221; event handler in the PyLan class.  This function can be triggered from a menu item, or from the edit button in the main toolbar:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_edit_object</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when we want to edit the selected item</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-comment"># Get the selected object
	</span><span class="hl-identifier">model</span><span class="hl-default">, </span><span class="hl-identifier">selection_iter</span><span class="hl-default">, </span><span class="hl-identifier">todo_ob</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">get_selected_object</span><span class="hl-brackets">()
	</span><span class="hl-identifier">if </span><span class="hl-brackets">((</span><span class="hl-identifier">selection_iter</span><span class="hl-brackets">) </span><span class="hl-identifier">and </span><span class="hl-brackets">(</span><span class="hl-identifier">model</span><span class="hl-brackets">) </span><span class="hl-identifier">and </span><span class="hl-brackets">(</span><span class="hl-identifier">todo_ob</span><span class="hl-brackets">))</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">All right something and we have all the needed data</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">edit_object</span><span class="hl-brackets">(</span><span class="hl-identifier">todo_ob</span><span class="hl-code">, </span><span class="hl-identifier">model</span><span class="hl-code">, </span><span class="hl-identifier">selection_iter</span><span class="hl-brackets">)</span></pre></div></div>
<p>Pretty simple, we call a helper function get_selected_object() which does exactly that, gets the selected object, the gtk.TreeIter associated with it, and the gtk.TreeModel that it is displayed in.  Then we call edit object with those parameters.  </p>
<p>get_selected_object() is a lot like the get_selected_category() function:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">get_selected_object</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Just a helper function that will give you the
	selected object, whether it is a todoItem or
	a todoTask.
	@returns A 3-tuple containing a reference to the
	gtk.TreeModel and a gtk.TreeIter pointing to the
	currently selected node. Just like
	gtk.TreeSelection.get_selected but with the todoObject
	being returned as well.</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-identifier">todo_ob</span><span class="hl-default"> = </span><span class="hl-reserved">None
	</span><span class="hl-identifier">tcolumn</span><span class="hl-default">, </span><span class="hl-identifier">pos</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">find_todoColumn</span><span class="hl-brackets">(</span><span class="hl-identifier">COL_OBJECT</span><span class="hl-brackets">)
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-reserved">not </span><span class="hl-identifier">tcolumn</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-reserved">return None</span><span class="hl-default">,</span><span class="hl-reserved">None</span><span class="hl-default">,</span><span class="hl-reserved">None

	</span><span class="hl-comment">#Get the current selection in the gtk.TreeView
	</span><span class="hl-identifier">selection</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">todoTreeView</span><span class="hl-default">.</span><span class="hl-identifier">get_selection</span><span class="hl-brackets">()
	</span><span class="hl-comment"># Get the selection iter
	</span><span class="hl-identifier">model</span><span class="hl-default">, </span><span class="hl-identifier">selection_iter</span><span class="hl-default"> = </span><span class="hl-identifier">selection</span><span class="hl-default">.</span><span class="hl-identifier">get_selected</span><span class="hl-brackets">()
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">selection_iter </span><span class="hl-reserved">and </span><span class="hl-identifier">model</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-comment">#Something is selected so get the object
		</span><span class="hl-identifier">todo_ob</span><span class="hl-default"> = </span><span class="hl-identifier">model</span><span class="hl-default">.</span><span class="hl-identifier">get_value</span><span class="hl-brackets">(</span><span class="hl-identifier">selection_iter</span><span class="hl-code">, </span><span class="hl-identifier">tcolumn</span><span class="hl-code">.</span><span class="hl-identifier">pos</span><span class="hl-brackets">)
	</span><span class="hl-reserved">return </span><span class="hl-identifier">model</span><span class="hl-default">, </span><span class="hl-identifier">selection_iter</span><span class="hl-default">, </span><span class="hl-identifier">todo_ob</span></pre></div></div>
<p>The edit_object() function is equally as easy:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">edit_object</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">todo_object</span><span class="hl-code">, </span><span class="hl-identifier">model</span><span class="hl-code">, </span><span class="hl-identifier">object_iter</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Helper function used to edit an object in the tree.
	@param todo_object - todoBase - The object that we
	are editing.
	@param model - gtk.TreeModel - The model for the tree.
	@param object_iter - gtk.TreeIter representing the
	todo_object's position in the tree.
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-comment">#Just make sure that they are all correct
	</span><span class="hl-identifier">if </span><span class="hl-brackets">((</span><span class="hl-identifier">todo_object</span><span class="hl-brackets">) </span><span class="hl-identifier">and </span><span class="hl-brackets">(</span><span class="hl-identifier">model</span><span class="hl-brackets">) </span><span class="hl-identifier">and </span><span class="hl-brackets">(</span><span class="hl-identifier">object_iter</span><span class="hl-brackets">))</span><span class="hl-default">:
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">todo_object</span><span class="hl-code">.</span><span class="hl-identifier">type</span><span class="hl-code"> == </span><span class="hl-identifier">todo</span><span class="hl-code">.</span><span class="hl-identifier">CATEGORY</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-comment">#Edit category
			</span><span class="hl-identifier">name</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">show_category_dialog</span><span class="hl-brackets">(</span><span class="hl-identifier">todo_object</span><span class="hl-code">.</span><span class="hl-identifier">name</span><span class="hl-brackets">)
			</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">name</span><span class="hl-brackets">)</span><span class="hl-default">:
				</span><span class="hl-identifier">todo_object</span><span class="hl-default">.</span><span class="hl-identifier">name</span><span class="hl-default"> = </span><span class="hl-identifier">name
				todo_object</span><span class="hl-default">.</span><span class="hl-identifier">set_tree_values</span><span class="hl-brackets">(</span><span class="hl-identifier">model</span><span class="hl-code">
					, </span><span class="hl-identifier">object_iter</span><span class="hl-code">
					, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">__tree_columns</span><span class="hl-brackets">)
		</span><span class="hl-reserved">else</span><span class="hl-default">:
			</span><span class="hl-comment">#Edit task
			</span><span class="hl-identifier">task_dialog</span><span class="hl-default"> = </span><span class="hl-identifier">todo</span><span class="hl-default">.</span><span class="hl-identifier">TaskDialog</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">gladefile</span><span class="hl-code">, </span><span class="hl-identifier">todo_object</span><span class="hl-brackets">)
			</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">task_dialog</span><span class="hl-code">.</span><span class="hl-identifier">run</span><span class="hl-brackets">()</span><span class="hl-code"> == </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_OK</span><span class="hl-brackets">)</span><span class="hl-default">:
				</span><span class="hl-identifier">todo_object</span><span class="hl-default">.</span><span class="hl-identifier">set_tree_values</span><span class="hl-brackets">(</span><span class="hl-identifier">model</span><span class="hl-code">
					, </span><span class="hl-identifier">object_iter</span><span class="hl-code">
					, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">__tree_columns</span><span class="hl-brackets">)</span></pre></div></div>
<p>So what we do is ensure that the parameters passed in are valid, then we check the type of the object, and then show the correct dialog.  If the user presses the Ok button then we update the data that is stored in the model by calling a new function set_tree_values():</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">set_tree_values</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">tree_model</span><span class="hl-code">, </span><span class="hl-identifier">iter</span><span class="hl-code">, </span><span class="hl-identifier">todoColumnList</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This is used to set the values in a tree.
	For whatever reason you cannot use the same list that you
	use to append items into the tree?  I don't know why.
	@param tree - gtk.TreeStore - The tree store that we will be
	setting the values in
	@param iter - gtk.TreeIter - The item in the tree
	@param todoColumnList - list - A list of todoColumn items.
	Their type member should use used to determine the order
	of the returned list.</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">lst_values</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">get_column_list</span><span class="hl-brackets">(</span><span class="hl-identifier">todoColumnList</span><span class="hl-brackets">)
	</span><span class="hl-identifier">count</span><span class="hl-default"> = </span><span class="hl-number">0
	</span><span class="hl-reserved">for </span><span class="hl-identifier">value </span><span class="hl-reserved">in </span><span class="hl-identifier">lst_values</span><span class="hl-default">:
		</span><span class="hl-identifier">tree_model</span><span class="hl-default">.</span><span class="hl-identifier">set_value</span><span class="hl-brackets">(</span><span class="hl-identifier">iter</span><span class="hl-code">, </span><span class="hl-identifier">count</span><span class="hl-code">, </span><span class="hl-identifier">value</span><span class="hl-brackets">)
		</span><span class="hl-identifier">count</span><span class="hl-default"> += </span><span class="hl-number">1</span></pre></div></div>
<p>This function is defined in the todo.todoBase class, which is the base class for both the todo.Task and todo.Caegory.  It can be overwritten by either if they want to perform some special processing, but by default it does everything that we need.  It gets the list of values to display in the tree, and then loops through that list setting each value in the model.</p>
<p>So that&#8217;s basically editing items, not too difficult.  The only other feature that I added was allowing you to edit an item when you double-click on in the gtk.TreeView.  I was slightly surprised to find out that there was no &#8220;double-click&#8221; signal for the gtk.TreeView instead one has to handle the <a href="http://www.pygtk.org/docs/pygtk/class-gtktreeview.html#signal-gtktreeview--row-activated">row-activated</a> signal:</p>
<blockquote><p>
The &#8220;row-activated&#8221; signal is emitted when the row_activated() method is called or the user double clicks a treeview row. &#8220;row-activated&#8221; is also emitted when a non-editable row is selected and one of the keys: Space, Shift+Space, Return or Enter is pressed.
</p></blockquote>
<p>So we can handle the &#8220;row-activated&#8221; signal like this:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_todoTree_row_activated</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">tree_view</span><span class="hl-code">, </span><span class="hl-identifier">path</span><span class="hl-code">, </span><span class="hl-identifier">tree_column</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This is called when a row is &quot;activated&quot; in the tree
	view.  It happens when the user double clicks on an item
	in the tree.  We will use it to edit the item.
	@param tree_view gtk.TreeView - The Tree
	@param path - string - The path string
	@param tree_column - gtk.TreeViewColumn - The column that
	was clicked on.
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-comment">#Get the column of the object
	</span><span class="hl-identifier">tcolumn</span><span class="hl-default">, </span><span class="hl-identifier">pos</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">find_todoColumn</span><span class="hl-brackets">(</span><span class="hl-identifier">COL_OBJECT</span><span class="hl-brackets">)
	</span><span class="hl-identifier">model</span><span class="hl-default"> = </span><span class="hl-identifier">tree_view</span><span class="hl-default">.</span><span class="hl-identifier">get_model</span><span class="hl-brackets">()
	</span><span class="hl-identifier">if </span><span class="hl-brackets">((</span><span class="hl-identifier">tcolumn</span><span class="hl-brackets">) </span><span class="hl-identifier">and </span><span class="hl-brackets">(</span><span class="hl-identifier">model</span><span class="hl-brackets">))</span><span class="hl-default">:
		</span><span class="hl-comment">#Now get the selection iter from the path
		</span><span class="hl-identifier">selection_iter</span><span class="hl-default"> = </span><span class="hl-identifier">model</span><span class="hl-default">.</span><span class="hl-identifier">get_iter</span><span class="hl-brackets">(</span><span class="hl-identifier">path</span><span class="hl-brackets">)
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">selection_iter</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-comment">#Now that we have the selection let's get the object
			</span><span class="hl-identifier">todo_ob</span><span class="hl-default"> = </span><span class="hl-identifier">model</span><span class="hl-default">.</span><span class="hl-identifier">get_value</span><span class="hl-brackets">(</span><span class="hl-identifier">selection_iter</span><span class="hl-code">, </span><span class="hl-identifier">tcolumn</span><span class="hl-code">.</span><span class="hl-identifier">pos</span><span class="hl-brackets">)
			</span><span class="hl-comment">#Now lets edit the object
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">edit_object</span><span class="hl-brackets">(</span><span class="hl-identifier">todo_ob</span><span class="hl-code">, </span><span class="hl-identifier">model</span><span class="hl-code">, </span><span class="hl-identifier">selection_iter</span><span class="hl-brackets">)</span></pre></div></div>
<p>First we get the model and the object column.  Then we use the path that was passed to the signal handler to get the gtk.TreeIter that matches it. Then we get the todo object and we edit it.  </p>
<p>Pretty simple, and now instead of having to find the edit button in the toolbar you can simple double-click on an item in order to edit it.</p>
<h2><a name="PangoMarkup"></a>Pango markup in the gtk.TreeView</h2>
<p>Since you currently cannot tell the difference between a category and a task I decided to make the category titles bold so that you can quickly tell the difference.</p>
<p>This is actually quite easy to do once you know <i>what</i> you need to do.  It took me a little while to find information on how to do this, but once I was able to find the information implementing it was pretty simple.</p>
<p>The first thing that we need to do is update the todoColumn object to have a new member variable: markup. This lets us know that this column will display markup instead of simple text.</p>
<p>So now the create_column() function in the todoColumn class, which was added to handle the creating of columns, looks like this:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">create_column</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:

	</span><span class="hl-identifier">column</span><span class="hl-default"> = </span><span class="hl-reserved">None
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">visible</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">markup</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-identifier">column</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">TreeViewColumn</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">name</span><span class="hl-code">
					, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">cellrenderer</span><span class="hl-code">
					, </span><span class="hl-identifier">text</span><span class="hl-code"> = </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">pos</span><span class="hl-brackets">)
		</span><span class="hl-reserved">else</span><span class="hl-default">:
			</span><span class="hl-identifier">column</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">TreeViewColumn</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">name</span><span class="hl-code">
					, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">cellrenderer</span><span class="hl-code">
					, </span><span class="hl-identifier">markup</span><span class="hl-code"> = </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">pos</span><span class="hl-brackets">)

	</span><span class="hl-reserved">return </span><span class="hl-identifier">column</span></pre></div></div>
<p>So if the member variable markup is set to True, then instead of getting the text from the specified position in the model, the column gets the markup from that position.</p>
<p>Then when the todo.Category populates the tree with it&#8217;s values in the get_coloumn_list() it returns the title with markup:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">get_column_list</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">todoColumnList</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is used to get the display list
	for the todoTree. The todoCOlumnList controls the
	order that the list will be returned in.
	@param todoColumnList - list - A list of todoColumn items.
	Their type member should use used to determine the order
	of the returned list.
	@returns list - A list for the todoTree.
	</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-identifier">lst_return</span><span class="hl-default"> = </span><span class="hl-brackets">[]
	</span><span class="hl-comment"># Loop through the columns and create the return list
	</span><span class="hl-reserved">for </span><span class="hl-identifier">item_column </span><span class="hl-reserved">in </span><span class="hl-identifier">todoColumnList</span><span class="hl-default">:
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-code"> == </span><span class="hl-identifier">pyLan</span><span class="hl-code">.</span><span class="hl-identifier">COL_OBJECT</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)
		</span><span class="hl-identifier">elif </span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-code"> == </span><span class="hl-identifier">pyLan</span><span class="hl-code">.</span><span class="hl-identifier">COL_OBJECT_TYPE</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">type</span><span class="hl-brackets">)
		</span><span class="hl-identifier">elif </span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-code"> == </span><span class="hl-identifier">pyLan</span><span class="hl-code">.</span><span class="hl-identifier">COL_TITLE</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">&lt;b&gt;%s&lt;/b&gt;</span><span class="hl-quotes">&quot;</span><span class="hl-code"> % </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">name</span><span class="hl-brackets">)
		</span><span class="hl-reserved">else</span><span class="hl-default">:
			</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)
	</span><span class="hl-reserved">return </span><span class="hl-identifier">lst_return</span></pre></div></div>
<p>So pretty simple, we wrap the title in the bold tags and then it displays as bold.</p>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/pylan_01/pylan_01_08.png" alt="Python GTD pyGTK" border="0" /></p>
<h2><a name="Conclusion"></a>Conclusion</h2>
<p>You can download the full source to this tutorial <a href="http://www.learningpython.com/sources/PyLan_02.tar.gz">here</a>.</p>
<p>So that&#8217;s it for this one, not as long or drawn out as the last one, but I think some interesting ground is covered.  Again this isn&#8217;t necessarily to teach you how to do everything that I&#8217;m doing, but to provide examples of how one <i>might</i> do things.</p>
<p>If you&#8217;re ever wanting to program something that you have never done before, I always think it&#8217;s nice to see examples of how other people do it.  Even if you don&#8217;t do it in the exact same manner, you will at least have an idea of how to approach the task.  Something that I really couldn&#8217;t find for a few features.  For example the  drop-down gtk.Calendar, which, come to think of it, would make a really nice custom widget if anyone was thinking of creating one.</p>
<p>As always if you find any errors or have any questions feel free to leave a comment.  If however you have a general question about python or PyGTK I would appreciate it if you ask it in the <a href="http://www.learningpython.com/forums/">LearningPython</a> forums so that a possible solution to someone elses problem will not get buried in an unrelated area.</p>
<div style="float:right;margin:0px 0px 0px 0px;"><a href="http://www.google.com/reader/link?url=http://www.learningpython.com/2007/03/11/pylan-a-gtd-todo-application-written-in-python-and-pygtk-part-two/&title=PyLan a GTD todo application written in python and PyGTK - part two&srcTitle=learning python&srcURL=http://www.learningpython.com"target="_blank" rel=""><img border="0" src="http://www.learningpython.com/wp-content/plugins/wp-google-buzz/icon/12.png" style="opacity:1;filter:alpha(opacity=100)" onmouseover="this.style.opacity=0.8;this.filters.alpha.opacity=70" onmouseout="this.style.opacity=1;this.filters.alpha.opacity=100"/> </a></div>]]></content:encoded>
			<wfw:commentRss>http://www.learningpython.com/2007/03/11/pylan-a-gtd-todo-application-written-in-python-and-pygtk-part-two/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>PyLan a GTD todo application written in python and PyGTK &#8211; part one</title>
		<link>http://www.learningpython.com/2007/02/17/pylan-a-gtd-todo-application-written-in-python-and-pygtk-part-one/</link>
		<comments>http://www.learningpython.com/2007/02/17/pylan-a-gtd-todo-application-written-in-python-and-pygtk-part-one/#comments</comments>
		<pubDate>Sat, 17 Feb 2007 22:47:03 +0000</pubDate>
		<dc:creator>selsine</dc:creator>
				<category><![CDATA[PyGTK]]></category>
		<category><![CDATA[PyLan]]></category>
		<category><![CDATA[glade]]></category>
		<category><![CDATA[gui]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://www.learningpython.com/?p=56</guid>
		<description><![CDATA[
			
				
			
		
Introduction
All right, many of you have probably heard of, or read, David Allen&#8217;s book Getting Things Done if not it&#8217;s a pretty interesting book about how to organize the things in your life.  
As I was reading it I thought that a simple todo list could be created using Python and PyGTK pretty easily, [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.learningpython.com%2F2007%2F02%2F17%2Fpylan-a-gtd-todo-application-written-in-python-and-pygtk-part-one%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.learningpython.com%2F2007%2F02%2F17%2Fpylan-a-gtd-todo-application-written-in-python-and-pygtk-part-one%2F&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<h2>Introduction</h2>
<p>All right, many of you have probably heard of, or read, David Allen&#8217;s book <a href="http://en.wikipedia.org/wiki/Getting_Things_Done">Getting Things Done</a> if not it&#8217;s a pretty interesting book about how to organize the things in your life.  </p>
<p>As I was reading it I thought that a simple todo list could be created using Python and PyGTK pretty easily, so I decided to create the application and write a tutorial surrounding it.  Since the actual application is quite large I have decided to break it up into a series of tutorials, each highlighting a few specific tasks.  The full goal of this series is to show everything that is required when creating a python/PyGTK based application, for the GUI setup  all the way to distribution.</p>
<p>These tutorials assume that you have a basic knowledge of python and PyGTK.  If you have not already done so it is recommended that you take a look at some of my previous PyGTK based tutorials as I will be glossing over much of that which is explained there:</p>
<ul>
<li><a href="http://www.learningpython.com/2006/05/07/creating-a-gui-using-pygtk-and-glade/">Creating a GUI using PyGTK and Glade</a></li>
<li><a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">Building an Application with PyGTK and Glade</a></li>
<li><a href="http://www.learningpython.com/2006/09/02/extending-our-pygtk-application/">Extending our PyGTK Application</a></li>
<li><a href="http://www.learningpython.com/2006/12/03/translating-your-pythonpygtk-application/">Translating your Python/PyGTK application</a></li>
<li><a href="http://www.learningpython.com/2006/07/25/writing-a-custom-widget-using-pygtk/">Writing a Custom Widget Using PyGTK</a></li>
<li><a href="http://www.learningpython.com/2006/08/19/wordpress-python-library/">WordPy offline blogging tool</a></li>
<li><a href="http://www.learningpython.com/2006/10/24/wordpy-02-using-xml-to-save-and-load-data/">WordPy 0.2 &#8211; Using XML to Save and Load Data</a></li>
</ul>
<h2>PyLan &#8211; part one</h2>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/pylan_01/pylan_01_01.png" alt="Python GTD pyGTK" border="0" /></p>
<p>So the application that we will be creating will be called PyLan, pronounced: plan.  In brief it will be used to create a tree of tasks that can be organized into categories and subcategories.</p>
<p>You can download the full source to this tutorial <a href="http://www.learningpython.com/sources/PyLan_01.tar.gz">here</a>.</p>
<p>Part one of this tutorial will introduce you to the idea of the application and create the basic shell for it.  It will outline the following:</p>
<ul>
<li>Creating the basic GUI in Glade</li>
<li>Working with a <a href="http://www.pygtk.org/docs/pygtk/class-gtktreeview.html">gtk.TreeView</a></li>
<li>Working with a <a href="http://www.pygtk.org/docs/pygtk/class-gtktreestore.html">gtk.TreeStore</a></li>
<li>Saving and loading the data from the tree using <a href="http://docs.python.org/lib/module-cPickle.html">cPickle</a>.</li>
<li>Using a <a href="http://www.pygtk.org/docs/pygtk/class-gtkmenutoolbutton.html">gtk.MenuToolButton</a></li>
</ul>
<p><span id="more-56"></span></p>
<p>The tutorial is organized into the following sections:</p>
<ol>
<li><a href="#TheGUI">The GUI</a></li>
<li><a href="#SettingItAllUp">The Code: Setting it all up</a></li>
<li><a href="#InitializingTheTree">The Code: Initializing the tree</a></li>
<li><a href="#TodoItems">The Code: todo items</a></li>
<li><a href="#AddingCategories">The Code: Adding Categories</a></li>
<li><a href="#RemovingCategories">The Code: Removing Categories</a></li>
<li><a href="#SavingAndLoadingWithcPickle">The Code: Saving and Loading with cPickle</a></li>
<li><a href="#ConnectingOurMenuWithThegtk.MenuToolButton">The Code: Connecting our menu with the gtk.MenuToolButton</a></li>
<li><a href="#Conclusion">Conclusion</a></li>
</ol>
<h2><a name="TheGUI"></a>The GUI</h2>
<p>The GUI will be created using Glade, it will be a relatively simple especially in this first part.  Since the glade project file is available I won&#8217;t go into too much detail about how to create this GUI, however I will outline the major steps that one could follow in case they would like to create their own GUI, or model a GUI after this one.</p>
<p><strong>The main window:</strong></p>
<ol>
<li>Create a new Glade Project and save it as pylan a folder named PyLan that you have created.</li>
<li>Create a window, and call it &#8220;mainWindow&#8221;, set its title to be PyLan.  If you are using Glade3 make sure that you set your window to be visible.  Add the GObject destroy signal handler like usual.  On the common tab set the width to be 400 and the height to be 300.</li>
<li>Add a vertical box to the window and give it 4 rows.</li>
<li>In the first row add a menu, call it &#8220;mainMenu&#8221;.  Make the Quit menu item&#8217;s menu handler the same as the Main Windows destroy signal handler.  For the rest of the File menu items, give them signal handlers using the following format: &#8220;on_file_new&#8221;, &#8220;on_file_open&#8221;, and so on.</li>
<li>In the second row add a tool bar with two items.</li>
<li>In the first spot add a GtkMenuToolButton, and set its name to &#8220;btnAdd&#8221;.  Make it a stock gtk-add button, and add a clicked signal handler with the name &#8220;on_add_category&#8221;.
</li>
<li>In the second spot at a GtkToolButton with the name &#8220;btnRemoveCategory&#8221;, being a stock gtk-remove button.  Set its clicked signal hander to be &#8220;on_remove_item&#8221;.</li>
<li>In the fourth row add a Status Bar.</li>
<li>In the Third row add a Tree View, set its name to be &#8220;todoTree&#8221;</li>
</ol>
<p><strong>The category dialog</strong></p>
<ol>
<li>Now create a Dialog with an Ok and a Cancel button.  Sets it&#8217;s name to be &#8220;categoryDialog&#8221;, and its title to be </li>
<li>&#8220;Category&#8221;.  Give it a default width and a width of 300.  Set its named icon to be: &#8220;stock_post-message&#8221;.</li>
<li>Add a GTKHBox to the dialog with two columns.</li>
<li>In the fist column add a GtkLabel with the label &#8220;Name:&#8221;. Give it an X Pad of 3 pixels.</li>
<li>In the second column add a GtkEntry with the name: &#8220;enName&#8221;</li>
</ol>
<p><strong>The Menu for the Add button</strong></p>
<p>Since we used a GtkMenuToolButton, we need to create the menu that we are going to associate with that button.  To do that we will use a GtkMenu.</p>
<ol>
<li>Create a GtkMenu and call it: &#8220;addMenu&#8221;</li>
<li>Add one Menu item to the menu with the label &#8220;Add _Category&#8221;.  Set the menu items handler &#8220;on_add_category&#8221;</li>
</ol>
<p>After all of that you should be left with something like this:</p>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/pylan_01/pylan_01_01.png" alt="Python GTD pyGTK" border="0" /></p>
<h2><a name="SettingItAllUp">The Code: Setting it all up</a></h2>
<p>Much of this initial code is taken from previous <a href="http://www.learningpython.com/tutorial-index/">pyGTK tutorials</a>, so if you are confused about any of this starting code, I suggest you take a look at the previous PyGTK tutorials where it is explained in depth.  Create this in a file called pyLan.py in the same directory where you saved your glade project.</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-comment">#!/usr/bin/env python

# PyLan - Python + PyGTK todo list
# Copyright (C) 2007 Mark Mruss &lt;selsine @gmail.com&gt;
# http://www.learningpython.com
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# If you find any bugs or have any suggestions email: selsine@gmail.com
# URL: http://www.learningpython.com

</span><span class="hl-identifier">__author__</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">Mark Mruss &lt;/selsine&gt;&lt;selsine @gmail.com&gt;</span><span class="hl-quotes">&quot;
</span><span class="hl-identifier">__version__</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">0.1</span><span class="hl-quotes">&quot;
</span><span class="hl-identifier">__date__</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">Date: 2007/02/17</span><span class="hl-quotes">&quot;
</span><span class="hl-identifier">__copyright__</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">Copyright (c) 2007 Mark Mruss</span><span class="hl-quotes">&quot;
</span><span class="hl-identifier">__license__</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">GPL</span><span class="hl-quotes">&quot;

</span><span class="hl-identifier">_</span><span class="hl-default"> = </span><span class="hl-reserved">lambda </span><span class="hl-identifier">x</span><span class="hl-default"> : </span><span class="hl-identifier">x
</span><span class="hl-reserved">try</span><span class="hl-default">:
 	</span><span class="hl-reserved">import </span><span class="hl-identifier">pygtk
  	pygtk</span><span class="hl-default">.</span><span class="hl-identifier">require</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">2.0</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
</span><span class="hl-reserved">except</span><span class="hl-default">:
  	</span><span class="hl-reserved">pass
try</span><span class="hl-default">:
	</span><span class="hl-reserved">import </span><span class="hl-identifier">sys
	</span><span class="hl-reserved">import </span><span class="hl-identifier">gtk
	</span><span class="hl-reserved">import </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">glade
	</span><span class="hl-reserved">import </span><span class="hl-identifier">gobject
	</span><span class="hl-reserved">import </span><span class="hl-identifier">os
	</span><span class="hl-reserved">import </span><span class="hl-identifier">locale
	</span><span class="hl-reserved">import </span><span class="hl-identifier">gettext
	</span><span class="hl-reserved">import </span><span class="hl-identifier">cPickle
	</span><span class="hl-reserved">import </span><span class="hl-identifier">helper
	</span><span class="hl-reserved">import </span><span class="hl-identifier">todo
</span><span class="hl-reserved">except ImportError</span><span class="hl-default">, </span><span class="hl-identifier">e</span><span class="hl-default">:
	</span><span class="hl-reserved">print </span><span class="hl-quotes">&quot;</span><span class="hl-string">Import error pyLan cannot start:</span><span class="hl-quotes">&quot;</span><span class="hl-default">, </span><span class="hl-identifier">e
	sys</span><span class="hl-default">.</span><span class="hl-identifier">exit</span><span class="hl-brackets">(</span><span class="hl-number">1</span><span class="hl-brackets">)

</span><span class="hl-identifier">FILE_EXT</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">pylan</span><span class="hl-quotes">&quot;
</span><span class="hl-identifier">APP_NAME</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">pyLan</span><span class="hl-quotes">&quot;


</span><span class="hl-reserved">class </span><span class="hl-identifier">pyLan</span><span class="hl-brackets">(</span><span class="hl-identifier">object</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">A simple python based GTD todo list</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:

		</span><span class="hl-comment">#Get the local path
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">local_path</span><span class="hl-default"> = </span><span class="hl-identifier">os</span><span class="hl-default">.</span><span class="hl-identifier">path</span><span class="hl-default">.</span><span class="hl-identifier">realpath</span><span class="hl-brackets">(</span><span class="hl-identifier">os</span><span class="hl-code">.</span><span class="hl-identifier">path</span><span class="hl-code">.</span><span class="hl-identifier">dirname</span><span class="hl-brackets">(</span><span class="hl-identifier">sys</span><span class="hl-code">.</span><span class="hl-identifier">argv</span><span class="hl-brackets">[</span><span class="hl-number">0</span><span class="hl-brackets">]))
		</span><span class="hl-comment">#Translation stuff
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">initialize_translation</span><span class="hl-brackets">()
		</span><span class="hl-comment">#Set the Glade file
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">gladefile</span><span class="hl-default"> = </span><span class="hl-identifier">os</span><span class="hl-default">.</span><span class="hl-identifier">path</span><span class="hl-default">.</span><span class="hl-identifier">join</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">local_path</span><span class="hl-code">, </span><span class="hl-quotes">&quot;</span><span class="hl-string">pylan.glade</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-comment">#Get the Main Widget Tree
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">glade</span><span class="hl-default">.</span><span class="hl-identifier">XML</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">gladefile</span><span class="hl-code">, </span><span class="hl-quotes">&quot;</span><span class="hl-string">mainWindow</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-comment">#Connect with yourself
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">signal_autoconnect</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)

		</span><span class="hl-comment">#init todo file
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">todo_file</span><span class="hl-default"> = </span><span class="hl-reserved">None
		</span><span class="hl-comment">#Get the Main Window
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">main_window</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">mainWindow</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">set_window_title_from_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">todo_file</span><span class="hl-brackets">)

	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">&quot;
	************************************************************
	* Initialize
	************************************************************
	</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">initialize_translation</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function initializes the possible translations</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-comment"># Init the list of languages to support
		</span><span class="hl-identifier">langs</span><span class="hl-default"> = </span><span class="hl-brackets">[]
		</span><span class="hl-comment">#Check the default locale
		</span><span class="hl-identifier">lc</span><span class="hl-default">, </span><span class="hl-identifier">encoding</span><span class="hl-default"> = </span><span class="hl-identifier">locale</span><span class="hl-default">.</span><span class="hl-identifier">getdefaultlocale</span><span class="hl-brackets">()
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">lc</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-comment">#If we have a default, it's the first in the list
			</span><span class="hl-identifier">langs</span><span class="hl-default"> = </span><span class="hl-brackets">[</span><span class="hl-identifier">lc</span><span class="hl-brackets">]
		</span><span class="hl-comment"># Now lets get all of the supported languages on the system
		</span><span class="hl-identifier">language</span><span class="hl-default"> = </span><span class="hl-identifier">os</span><span class="hl-default">.</span><span class="hl-identifier">environ</span><span class="hl-default">.</span><span class="hl-identifier">get</span><span class="hl-brackets">(</span><span class="hl-quotes">'</span><span class="hl-string">LANGUAGE</span><span class="hl-quotes">'</span><span class="hl-code">, </span><span class="hl-reserved">None</span><span class="hl-brackets">)
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">language</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">language comes back something like en_CA:en_US:en_GB:en
			on linuxy systems, on Win32 it's nothing, so we need to
			split it up into a list</span><span class="hl-quotes">&quot;&quot;&quot;
			</span><span class="hl-identifier">langs</span><span class="hl-default"> += </span><span class="hl-identifier">language</span><span class="hl-default">.</span><span class="hl-identifier">split</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">:</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Now add on to the back of the list the translations that we
		know that we have, our defaults</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">langs</span><span class="hl-default"> += </span><span class="hl-brackets">[</span><span class="hl-quotes">&quot;</span><span class="hl-string">en_CA</span><span class="hl-quotes">&quot;</span><span class="hl-code">, </span><span class="hl-quotes">&quot;</span><span class="hl-string">en_US</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">]

		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Now langs is a list of all of the languages that we are going
		to try to use.  First we check the default, then what the system
		told us, and finally the 'known' list</span><span class="hl-quotes">&quot;&quot;&quot;

		</span><span class="hl-identifier">gettext</span><span class="hl-default">.</span><span class="hl-identifier">bindtextdomain</span><span class="hl-brackets">(</span><span class="hl-identifier">APP_NAME</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">local_path</span><span class="hl-brackets">)
		</span><span class="hl-identifier">gettext</span><span class="hl-default">.</span><span class="hl-identifier">textdomain</span><span class="hl-brackets">(</span><span class="hl-identifier">APP_NAME</span><span class="hl-brackets">)
		</span><span class="hl-comment"># Get the language to use
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">lang</span><span class="hl-default"> = </span><span class="hl-identifier">gettext</span><span class="hl-default">.</span><span class="hl-identifier">translation</span><span class="hl-brackets">(</span><span class="hl-identifier">APP_NAME</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">local_path</span><span class="hl-code">
			, </span><span class="hl-identifier">languages</span><span class="hl-code">=</span><span class="hl-identifier">langs</span><span class="hl-code">, </span><span class="hl-identifier">fallback</span><span class="hl-code"> = </span><span class="hl-reserved">True</span><span class="hl-brackets">)
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Install the language, map _() (which we marked our
		strings to translate with) to self.lang.gettext() which will
		translate them.</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">gettext</span><span class="hl-default">.</span><span class="hl-identifier">install</span><span class="hl-brackets">(</span><span class="hl-identifier">APP_NAME</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">local_path</span><span class="hl-brackets">)

	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">&quot;
	********************************************************
	* Simple Helpers
	********************************************************
	</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">set_window_title_from_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">todo_file</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Set the windows title, take it from todo_file.
		@param todo_file - string - The todo file name that we will
		base the window title off of
		</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">todo_file</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">main_window</span><span class="hl-default">.</span><span class="hl-identifier">set_title</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">PyLan - %s</span><span class="hl-quotes">&quot;</span><span class="hl-code">
				% </span><span class="hl-brackets">(</span><span class="hl-identifier">os</span><span class="hl-code">.</span><span class="hl-identifier">path</span><span class="hl-code">.</span><span class="hl-identifier">basename</span><span class="hl-brackets">(</span><span class="hl-identifier">todo_file</span><span class="hl-brackets">)))
		</span><span class="hl-reserved">else</span><span class="hl-default">:
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">main_window</span><span class="hl-default">.</span><span class="hl-identifier">set_title</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">PyLan - Untitled</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">))


	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">
	************************************************************
	* Signal Handlers
	************************************************************
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-reserved">def </span><span class="hl-identifier">on_mainWindow_destroy</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the application is going to quit</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">main_quit</span><span class="hl-brackets">()

	</span><span class="hl-reserved">def </span><span class="hl-identifier">on_add_category</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the Add Category button is clicked.
		Can also be generally used to add a category</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-reserved">pass

	def </span><span class="hl-identifier">on_remove_item</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">called then the remove category button is clicked.
		Can also be generally used to remove a category or
		item.</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-reserved">pass

	def </span><span class="hl-identifier">on_file_new</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">File | New - Start a new project file, blank out
		the current project and start from scratch</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-reserved">pass


	def </span><span class="hl-identifier">on_file_open</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Function called to open a todo file</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-reserved">pass

	def </span><span class="hl-identifier">on_file_save</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">File | Save function - Save the Todo file</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-reserved">pass

	def </span><span class="hl-identifier">on_file_save_as</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">File | Save As function - Save the todo file</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-reserved">pass

if </span><span class="hl-identifier">__name__</span><span class="hl-default"> == </span><span class="hl-quotes">&quot;</span><span class="hl-string">__main__</span><span class="hl-quotes">&quot;</span><span class="hl-default">:
	</span><span class="hl-identifier">plan</span><span class="hl-default"> = </span><span class="hl-identifier">pyLan</span><span class="hl-brackets">()
	</span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">main</span><span class="hl-brackets">()</span></pre></div></div>
<p>If you run the code you should be greeted with the following:</p>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/pylan_01/pylan_01_04.png" alt="Python GTD pyGTK" border="0" /></p>
<p>If you are familiar with my other tutorials none of this code should be very surprising to you. The only exception is perhaps the way that we connect our functions with the signal handlers:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">signal_autoconnect</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span></pre></div></div>
<p>So instead of creating a dict with our functions and then passing it to the signal_autoconnect() function we simply pass our class to the function, and then any of our functions that match the signal handlers in the widget tree will be connected.  A very slick way to handle this if you ask me.</p>
<h2><a name="InitializingTheTree">The Code: Initializing the tree</a></h2>
<p>The next thing that we have to do is initialize our tree, i.e. set up the columns, create the model for the gtk.TreeView, and so on.  I struggled with how to set up the columns for a while since I knew that I wanted them to be dynamic and for the application to function properly no matter what order the columns were in.</p>
<p>As a result I decided to use a simple class and a list of those objects to define what columns are available and what is in each column.  I&#8217;m still not sure if this is the best way to handle it, but it meets most of my requirements and was relatively simple to implement.</p>
<p>The first thing that we need to do is create some &#8220;constants&#8221; near the top of our code right underneath out APP_NAME define:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Column IDs</span><span class="hl-quotes">&quot;&quot;&quot;
</span><span class="hl-identifier">COL_OBJECT</span><span class="hl-default"> = </span><span class="hl-number">0
</span><span class="hl-identifier">COL_OBJECT_TYPE</span><span class="hl-default"> = </span><span class="hl-number">1
</span><span class="hl-identifier">COL_TITLE</span><span class="hl-default"> = </span><span class="hl-number">2
</span><span class="hl-identifier">COL_DETAILS</span><span class="hl-default"> = </span><span class="hl-number">3
</span><span class="hl-identifier">COL_DUE</span><span class="hl-default"> = </span><span class="hl-number">4
</span><span class="hl-identifier">COL_PRIORITY</span><span class="hl-default"> = </span><span class="hl-number">5
</span><span class="hl-identifier">COL_COMPLETED</span><span class="hl-default"> = </span><span class="hl-number">6</span></pre></div></div>
<p>These are the IDs of the columns that we will have in our list.  For this tutorial most of these will remain unused, but as we progress more and more will be used.  The IDs are used to let us know what each column is.  So COL_OBJECT will be the column that holds the actual python object (as we did in the PyWine tutorial).  COL_OBJECT_TYPE will hold the objects type, and so on.</p>
<p>Then we need a simple class that will contain this information:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">class </span><span class="hl-identifier">todoColumn</span><span class="hl-brackets">(</span><span class="hl-identifier">object</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This is a class that represents a column in the todo tree.
	It is simply a helper class that makes it easier to inialize the
	tree.</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">ID</span><span class="hl-code">, </span><span class="hl-identifier">type</span><span class="hl-code">, </span><span class="hl-identifier">name</span><span class="hl-code">, </span><span class="hl-identifier">pos</span><span class="hl-code">, </span><span class="hl-identifier">visible</span><span class="hl-code">=</span><span class="hl-reserved">False</span><span class="hl-code">, </span><span class="hl-identifier">cellrenderer</span><span class="hl-code"> = </span><span class="hl-reserved">None</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">
		@param ID - int - The Columns ID
		@param type - int  - A gobject.TYPE_ for the gtk.TreeStore
		@param name - string - The name of the column
		@param pos - int - The index of the column.
		@param visible - boolean - Is the column visible or not?
		@param cellrenderer - gtk.CellRenderer - a constructor function
		for the column
		</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">ID</span><span class="hl-default"> = </span><span class="hl-identifier">ID
		self</span><span class="hl-default">.</span><span class="hl-identifier">type</span><span class="hl-default"> = </span><span class="hl-identifier">type
		self</span><span class="hl-default">.</span><span class="hl-identifier">name</span><span class="hl-default"> = </span><span class="hl-identifier">name
		self</span><span class="hl-default">.</span><span class="hl-identifier">pos</span><span class="hl-default"> = </span><span class="hl-identifier">pos
		self</span><span class="hl-default">.</span><span class="hl-identifier">visible</span><span class="hl-default"> = </span><span class="hl-identifier">visible
		self</span><span class="hl-default">.</span><span class="hl-identifier">cellrenderer</span><span class="hl-default"> = </span><span class="hl-identifier">cellrenderer
		self</span><span class="hl-default">.</span><span class="hl-identifier">colour</span><span class="hl-default"> = </span><span class="hl-number">0

	</span><span class="hl-reserved">def </span><span class="hl-identifier">__str__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-reserved">return </span><span class="hl-quotes">&quot;</span><span class="hl-string">&lt;todocolumn object: ID = %s type = %s name = %s pos = %d visible = %s cellrenderer = %s&gt;</span><span class="hl-quotes">&quot;</span><span class="hl-default"> % </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">type</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">name</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">pos</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">visible</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">cellrenderer</span><span class="hl-brackets">)</span></pre></div></div>
<p>This class should be created before the PyLan class.  Then at the beginning of the PyLan class we will create a hidden member at the beginnig of the __init__() function:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">The colum list, at one point this could be saved and loaded
		from a file.</span><span class="hl-quotes">&quot;&quot;&quot;
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__tree_columns</span><span class="hl-default"> = </span><span class="hl-brackets">[
			</span><span class="hl-identifier">todoColumn</span><span class="hl-brackets">(</span><span class="hl-identifier">COL_OBJECT</span><span class="hl-code">, </span><span class="hl-identifier">gobject</span><span class="hl-code">.</span><span class="hl-identifier">TYPE_PYOBJECT</span><span class="hl-code">, </span><span class="hl-quotes">&quot;</span><span class="hl-string">object</span><span class="hl-quotes">&quot;</span><span class="hl-code">, </span><span class="hl-number">0</span><span class="hl-brackets">)</span><span class="hl-code">
			, </span><span class="hl-identifier">todoColumn</span><span class="hl-brackets">(</span><span class="hl-identifier">COL_OBJECT_TYPE</span><span class="hl-code">, </span><span class="hl-identifier">gobject</span><span class="hl-code">.</span><span class="hl-identifier">TYPE_INT</span><span class="hl-code">, </span><span class="hl-quotes">&quot;</span><span class="hl-string">object_type</span><span class="hl-quotes">&quot;</span><span class="hl-code">, </span><span class="hl-number">1</span><span class="hl-brackets">)</span><span class="hl-code">
			, </span><span class="hl-identifier">todoColumn</span><span class="hl-brackets">(</span><span class="hl-identifier">COL_TITLE</span><span class="hl-code">, </span><span class="hl-identifier">gobject</span><span class="hl-code">.</span><span class="hl-identifier">TYPE_STRING</span><span class="hl-code">, </span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Title</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">, </span><span class="hl-number">2</span><span class="hl-code">, </span><span class="hl-reserved">True</span><span class="hl-code">, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">CellRendererText</span><span class="hl-brackets">())</span><span class="hl-code">
			, </span><span class="hl-identifier">todoColumn</span><span class="hl-brackets">(</span><span class="hl-identifier">COL_DETAILS</span><span class="hl-code">, </span><span class="hl-identifier">gobject</span><span class="hl-code">.</span><span class="hl-identifier">TYPE_STRING</span><span class="hl-code">, </span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Details</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">, </span><span class="hl-number">3</span><span class="hl-code">, </span><span class="hl-reserved">True</span><span class="hl-code">, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">CellRendererText</span><span class="hl-brackets">())</span><span class="hl-code">
			, </span><span class="hl-identifier">todoColumn</span><span class="hl-brackets">(</span><span class="hl-identifier">COL_DUE</span><span class="hl-code">, </span><span class="hl-identifier">gobject</span><span class="hl-code">.</span><span class="hl-identifier">TYPE_STRING</span><span class="hl-code">, </span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Due</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">, </span><span class="hl-number">4</span><span class="hl-code">, </span><span class="hl-reserved">True</span><span class="hl-code">, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">CellRendererText</span><span class="hl-brackets">())</span><span class="hl-code">
			, </span><span class="hl-identifier">todoColumn</span><span class="hl-brackets">(</span><span class="hl-identifier">COL_PRIORITY</span><span class="hl-code">, </span><span class="hl-identifier">gobject</span><span class="hl-code">.</span><span class="hl-identifier">TYPE_STRING</span><span class="hl-code">, </span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Priority</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">, </span><span class="hl-number">5</span><span class="hl-code">, </span><span class="hl-reserved">True</span><span class="hl-code">, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">CellRendererText</span><span class="hl-brackets">())</span><span class="hl-code">
			, </span><span class="hl-identifier">todoColumn</span><span class="hl-brackets">(</span><span class="hl-identifier">COL_COMPLETED</span><span class="hl-code">, </span><span class="hl-identifier">gobject</span><span class="hl-code">.</span><span class="hl-identifier">TYPE_STRING</span><span class="hl-code">, </span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Completed</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">, </span><span class="hl-number">6</span><span class="hl-code">, </span><span class="hl-reserved">True</span><span class="hl-code">, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">CellRendererText</span><span class="hl-brackets">())
				]</span></pre></div></div>
<p>So what we are doing is creating a list that defines the columns that will be added to our Tree.  As you can see when we create each todoColumn instance we specify everything that is needed to create that column in the tree.</p>
<p>Next we&#8217;ll add a function that we will use to initialize our tree.  This function will be called from our __init__() function:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-comment">#Connect with yourself
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">signal_autoconnect</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)

</span><span class="hl-comment">#Initialize the todo Tree
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">initialize_todo_tree</span><span class="hl-brackets">()</span></pre></div></div>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">initialize_todo_tree</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when we want to initialize the tree.
		</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">tree_type_list</span><span class="hl-default"> = </span><span class="hl-brackets">[] </span><span class="hl-comment">#For creating the TreeStore
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__column_dict</span><span class="hl-default"> = {} </span><span class="hl-comment">#For easy access later on

		#Get the treeView from the widget Tree
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">todoTreeView</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">todoTree</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-comment">#Make it so that the colours of each row can alternate
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">todoTreeView</span><span class="hl-default">.</span><span class="hl-identifier">set_rules_hint</span><span class="hl-brackets">(</span><span class="hl-reserved">True</span><span class="hl-brackets">)

		</span><span class="hl-comment"># Loop through the columns and initialize the Tree
		</span><span class="hl-reserved">for </span><span class="hl-identifier">item_column </span><span class="hl-reserved">in </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__tree_columns</span><span class="hl-default">:
			</span><span class="hl-comment">#Add the column to the column dict
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__column_dict</span><span class="hl-brackets">[</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-brackets">]</span><span class="hl-default"> = </span><span class="hl-identifier">item_column
			</span><span class="hl-comment">#Save the type for gtk.TreeStore creation
			</span><span class="hl-identifier">tree_type_list</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">type</span><span class="hl-brackets">)
			</span><span class="hl-comment">#is it visible?
			</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">visible</span><span class="hl-brackets">)</span><span class="hl-default">:
				</span><span class="hl-comment">#Create the Column
				</span><span class="hl-identifier">column</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">TreeViewColumn</span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">name</span><span class="hl-code">
					, </span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">cellrenderer</span><span class="hl-code">
					, </span><span class="hl-identifier">text</span><span class="hl-code">=</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">pos</span><span class="hl-brackets">)

				</span><span class="hl-identifier">column</span><span class="hl-default">.</span><span class="hl-identifier">set_resizable</span><span class="hl-brackets">(</span><span class="hl-reserved">True</span><span class="hl-brackets">)
				</span><span class="hl-identifier">column</span><span class="hl-default">.</span><span class="hl-identifier">set_sort_column_id</span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">pos</span><span class="hl-brackets">)
				</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">todoTreeView</span><span class="hl-default">.</span><span class="hl-identifier">append_column</span><span class="hl-brackets">(</span><span class="hl-identifier">column</span><span class="hl-brackets">)

		</span><span class="hl-comment">#Create the gtk.TreeStore Model to use with the todoTree
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">todoTree</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">TreeStore</span><span class="hl-brackets">(</span><span class="hl-code">*</span><span class="hl-identifier">tree_type_list</span><span class="hl-brackets">)
		</span><span class="hl-comment">#Attache the model to the treeView
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">todoTreeView</span><span class="hl-default">.</span><span class="hl-identifier">set_model</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">todoTree</span><span class="hl-brackets">)</span></pre></div></div>
<p>So what we do in this function is first get our <a href="http://www.pygtk.org/docs/pygtk/class-gtktreeview.html">gtk.TreeView</a> widget from our widget tree. Then we loop through the column list that we created, and add the column&#8217;s type to the tree_type_list, which will be used to create our <a href="http://www.pygtk.org/docs/pygtk/class-gtktreestore.html">gtk.TreeStore</a>.  If the todoColum is visible we need to create a <a href="http://www.pygtk.org/docs/pygtk/class-gtktreeviewcolumn.html">gtk.TreeViewColum</a> and then add it to the gtk.TreeView.</p>
<p>After we have looped through the column list we create the gtk.TreeStore (our model for the gtk.TreeView) based on all the types in our column list.  Then we set that model as the model for our gtk.TreeView.</p>
<p>This may seem a bit confusing and if you have any problems understanding take a look at the <a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">Building an Application with PyGTK and Glade</a> tutorial where the relationship between gtk.TreeViews, gtk.TreeViewColumns, gtk.CellRenderers (which we define in our todoColumn), and gtk.TreeModels is described.  Basically the model (the gtk.TreeStore in this case) is the data.  The View (gtk.TreeView, gtk.TreeViewColumn, gtk.CellRenderer) is what is used to display the data.  </p>
<p>This means that you could have multiple views for the same data.  We&#8217;re not doing that in this application so far, but the idea is quite powerful.  We could, for example, use it to create a &#8220;simple&#8221; and &#8220;advanced&#8221; view for our data, or other &#8220;filtered&#8221; views.</p>
<p>Now if we run the code we get the following:</p>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/pylan_01/pylan_01_01.png" alt="Python GTD pyGTK" border="0" /></p>
<p>It&#8217;s the same as it was before, except now we have columns in our gtk.TreeView.</p>
<h2><a name="TodoItems">The Code: todo items</a></h2>
<p>Now that we have our tree defined, we need to create the objects that will store the data.  The data will be divided into two types (for now) a category and an task.</p>
<p>A Category is simply a container for other categories or items.  An task is an actual task that you need to do.</p>
<p>So a category could be &#8220;Work&#8221; and in that category you might add another category called &#8220;Project X&#8221; and in &#8220;Project X&#8221; you may add the task &#8220;Implement feature y.&#8221;  For this tutorial we will not be working with tasks, we will simply be working with categories.</p>
<p>We will create a new file in the same directory where we created our pyLan.py file.  The file will be named todo.py.  We will start off be defining the todoBase class which we will use as the base class for both the Category and Task classes:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-comment">#!/usr/bin/env python

</span><span class="hl-reserved">import </span><span class="hl-identifier">pyLan
</span><span class="hl-reserved">import </span><span class="hl-identifier">helper

CATEGORY</span><span class="hl-default"> = </span><span class="hl-number">0
</span><span class="hl-identifier">TASK</span><span class="hl-default">= </span><span class="hl-number">1

</span><span class="hl-reserved">class </span><span class="hl-identifier">todoBase</span><span class="hl-brackets">(</span><span class="hl-identifier">object</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This is the base class for the todoCategory and
	the todoTask.
	</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-comment">#Type property
	</span><span class="hl-reserved">def </span><span class="hl-identifier">__get_type</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-reserved">return </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_type
	type</span><span class="hl-default"> = </span><span class="hl-builtin">property</span><span class="hl-brackets">(</span><span class="hl-identifier">__get_type</span><span class="hl-brackets">)

	</span><span class="hl-reserved">def </span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">type</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">The only way to set the type is through the
		initialization</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_type</span><span class="hl-default"> = </span><span class="hl-identifier">type

	</span><span class="hl-reserved">def </span><span class="hl-identifier">get_column_list</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">todoColumnList</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is used to get the display list
		for the todoTree. The todoCOlumnList controls the
		order that the list will be returned in. Used as the
		second param in the gtk.TreeStore.append function.
		@param todoColumnList - list - A list of todoColumn items.
		Their type member should use used to determine the order
		of the returned list.
		@returns list - A list for the todoTree.
		</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-reserved">pass

	def </span><span class="hl-identifier">add_to_tree</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">tree</span><span class="hl-code">, </span><span class="hl-identifier">parent</span><span class="hl-code">, </span><span class="hl-identifier">todoColumnList</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is used to add an item to a
		gtk.TreeStore, usually when loading.  All
		children will be added as well
		@param tree - gtk.TreeStore - The tree store that
		we wil be adding to.
		@param parent gtk.TreeIter - The parent of this
		item.
		@param todoColumnList - list - A list of todoColumn items.
		Their type member should use used to determine the order
		of the returned list.
		</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">tree</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">parent</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">get_column_list</span><span class="hl-brackets">(</span><span class="hl-identifier">todoColumnList</span><span class="hl-brackets">))</span></pre></div></div>
<p>So the todoBase is simply a class with a type property (either CATEGORY, or TASK) that can only be set in the constructor and two functions get_column_list() and add_to_tree().</p>
<p>get_column_list() is used to create the second parameter for the <a href="http://www.pygtk.org/docs/pygtk/class-gtktreestore.html#method-gtktreestore--append">gtk.TreeStore.append()</a> function &#8220;a tuple or list containing ordered column values to be set in the new row.&#8221;</p>
<p>add_to_tree() is used to add the todo item to the actual tree.  For the category class that we are going to create this is used to ensure that any children (i.e. categories or tasks) will also be added to the tree at the same time.</p>
<p>The todoCategory class is defined as follows:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">class </span><span class="hl-identifier">todoCategory</span><span class="hl-brackets">(</span><span class="hl-identifier">todoBase</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This is a category in the todoTree.  It can have child categories
	or child tasks.</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-comment">#Name property
	</span><span class="hl-reserved">def </span><span class="hl-identifier">__get_name</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-reserved">return </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_name
	</span><span class="hl-reserved">def </span><span class="hl-identifier">__set_name</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">name</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_name</span><span class="hl-default"> = </span><span class="hl-identifier">name
	name</span><span class="hl-default"> = </span><span class="hl-builtin">property</span><span class="hl-brackets">(</span><span class="hl-identifier">__get_name</span><span class="hl-code">, </span><span class="hl-identifier">__set_name</span><span class="hl-brackets">)

	</span><span class="hl-reserved">def </span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">name</span><span class="hl-brackets">)</span><span class="hl-default">:

		</span><span class="hl-comment">#init variables
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_name</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;&quot;
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">name</span><span class="hl-default"> = </span><span class="hl-identifier">name
		</span><span class="hl-comment">#children
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_children</span><span class="hl-default"> = </span><span class="hl-brackets">[]
		</span><span class="hl-comment">#init base
		</span><span class="hl-identifier">todoBase</span><span class="hl-default">.</span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">CATEGORY</span><span class="hl-brackets">)

	</span><span class="hl-reserved">def </span><span class="hl-identifier">__str__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-reserved">return </span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">&lt;todocategory object: name = %s num_children = %d&gt;</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-default"> % </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">name</span><span class="hl-code">, </span><span class="hl-builtin">len</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">__m_children</span><span class="hl-brackets">))

	</span><span class="hl-reserved">def </span><span class="hl-identifier">add_child</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">todo_object</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Add a child to the category.
		@param todo_object - Either a todoCategory or a
		todoTask.  This will be a child of the category.
		</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_children</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">todo_object</span><span class="hl-brackets">)

	</span><span class="hl-reserved">def </span><span class="hl-identifier">remove_child</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">todo_object</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Removes a child from the category.
		@param todo_object - Either a todoCategory or a
		todoTask.  This will be a removed.
		</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-reserved">try</span><span class="hl-default">:
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_children</span><span class="hl-default">.</span><span class="hl-identifier">remove</span><span class="hl-brackets">(</span><span class="hl-identifier">todo_object</span><span class="hl-brackets">)
		</span><span class="hl-reserved">except ValueError</span><span class="hl-default">, </span><span class="hl-identifier">e</span><span class="hl-default">:
			</span><span class="hl-identifier">helper</span><span class="hl-default">.</span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Error removing child %s = %s</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code"> % </span><span class="hl-brackets">(</span><span class="hl-identifier">todo_object</span><span class="hl-code">, </span><span class="hl-identifier">e</span><span class="hl-brackets">))


	</span><span class="hl-reserved">def </span><span class="hl-identifier">get_column_list</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">todoColumnList</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is used to get the display list
		for the todoTree. The todoCOlumnList controls the
		order that the list will be returned in.
		@param todoColumnList - list - A list of todoColumn items.
		Their type member should use used to determine the order
		of the returned list.
		@returns list - A list for the todoTree.
		</span><span class="hl-quotes">&quot;&quot;&quot;

		</span><span class="hl-identifier">lst_return</span><span class="hl-default"> = </span><span class="hl-brackets">[]
		</span><span class="hl-comment"># Loop through the columns and create the return list
		</span><span class="hl-reserved">for </span><span class="hl-identifier">item_column </span><span class="hl-reserved">in </span><span class="hl-identifier">todoColumnList</span><span class="hl-default">:
			</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-code"> == </span><span class="hl-identifier">pyLan</span><span class="hl-code">.</span><span class="hl-identifier">COL_OBJECT</span><span class="hl-brackets">)</span><span class="hl-default">:
				</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)
			</span><span class="hl-identifier">elif </span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-code"> == </span><span class="hl-identifier">pyLan</span><span class="hl-code">.</span><span class="hl-identifier">COL_OBJECT_TYPE</span><span class="hl-brackets">)</span><span class="hl-default">:
				</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">type</span><span class="hl-brackets">)
			</span><span class="hl-identifier">elif </span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-code"> == </span><span class="hl-identifier">pyLan</span><span class="hl-code">.</span><span class="hl-identifier">COL_TITLE</span><span class="hl-brackets">)</span><span class="hl-default">:
				</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">name</span><span class="hl-brackets">)
			</span><span class="hl-reserved">else</span><span class="hl-default">:
				</span><span class="hl-identifier">lst_return</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)
		</span><span class="hl-reserved">return </span><span class="hl-identifier">lst_return

	</span><span class="hl-reserved">def </span><span class="hl-identifier">add_to_tree</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">tree</span><span class="hl-code">, </span><span class="hl-identifier">parent</span><span class="hl-code">, </span><span class="hl-identifier">todoColumnList</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is used to add an item to a
		gtk.TreeStore, usually when loading.  All
		children will be added as well
		@param tree - gtk.TreeStore - The tree store that
		we will be adding to.
		@param parent gtk.TreeIter - The parent of this
		item.
		@param todoColumnList - list - A list of todoColumn items.
		Their type member should use used to determine the order
		of the returned list.
		</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">insert_iter</span><span class="hl-default"> = </span><span class="hl-identifier">tree</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">parent</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">get_column_list</span><span class="hl-brackets">(</span><span class="hl-identifier">todoColumnList</span><span class="hl-brackets">))
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">insert_iter</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-reserved">for </span><span class="hl-identifier">child </span><span class="hl-reserved">in </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__m_children</span><span class="hl-default">:
				</span><span class="hl-identifier">child</span><span class="hl-default">.</span><span class="hl-identifier">add_to_tree</span><span class="hl-brackets">(</span><span class="hl-identifier">tree</span><span class="hl-code">, </span><span class="hl-identifier">insert_iter</span><span class="hl-code">, </span><span class="hl-identifier">todoColumnList</span><span class="hl-brackets">)
		</span><span class="hl-reserved">else</span><span class="hl-default">:
			</span><span class="hl-identifier">helper</span><span class="hl-default">.</span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Error loading category: %s</span><span class="hl-quotes">&quot;</span><span class="hl-code"> % </span><span class="hl-identifier">self</span><span class="hl-brackets">))</span></pre></div></div>
<p>You can see that it sets its type by initializing the base class and passing it the CATEGORY constant.  It also has some additional members, the name property and the private member __m_children.  Name is the name of the category and __m_children is a list of todoBase objects that are the children of this category.</p>
<p>It implements two functions add_child() and remove_child() that are pretty self explanatory, and it overrides the get_column_list() and add_to_tree() functions from the todoBase.</p>
<p>You can see that in the get_column_list() function that the todoCategory only specifies three columns, for the rest is passes empty strings.  It should be pretty obvious what the function is doing.</p>
<p>add_to_tree() is also overridden.  What it does is add itself to the tree, if that succeeds it then loops through all of its children and then adds them to the tree as well.  If that fails it calls the show_error_dlg() function from the helper module that has not been explained yet.  All you need to know is that show_error_dlg() pops up a simple error dialog.</p>
<h2><a name="AddingCategories">The Code: Adding Categories</a></h2>
<p>Now that we have our category classes defined we need to work on adding them to the actual tree.  To do that we will want to use our categoryDialog.  We will show that to the user and let them set the name of the category.  We will create a function called show_category_dialog() to do this for us:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">show_category_dialog</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">name</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function shows the category dialog.
	@param name - string - The name to display in
	category dialog by default.
	@returns - If OK is pressed the name typed on the
	dialog. If Cancel is pressed None is returned.
	</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-comment">#init to cancel
	</span><span class="hl-identifier">name_return</span><span class="hl-default"> = </span><span class="hl-reserved">None

	</span><span class="hl-comment">#load the dialog from the glade file
	</span><span class="hl-identifier">wTree</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">glade</span><span class="hl-default">.</span><span class="hl-identifier">XML</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">gladefile</span><span class="hl-code">, </span><span class="hl-quotes">&quot;</span><span class="hl-string">categoryDialog</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-comment">#Get the actual dialog widget
	</span><span class="hl-identifier">dlg</span><span class="hl-default"> = </span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">categoryDialog</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-comment">#Get all of the Entry Widgets and set their text
	</span><span class="hl-identifier">enName</span><span class="hl-default"> = </span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">enName</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">enName</span><span class="hl-default">.</span><span class="hl-identifier">set_text</span><span class="hl-brackets">(</span><span class="hl-identifier">name</span><span class="hl-brackets">)

	</span><span class="hl-comment">#run the dialog and store the response
	</span><span class="hl-identifier">result</span><span class="hl-default"> = </span><span class="hl-identifier">dlg</span><span class="hl-default">.</span><span class="hl-identifier">run</span><span class="hl-brackets">()
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">result</span><span class="hl-code">==</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_OK</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-comment">#get the value of the entry fields
		</span><span class="hl-identifier">name_return</span><span class="hl-default"> = </span><span class="hl-identifier">enName</span><span class="hl-default">.</span><span class="hl-identifier">get_text</span><span class="hl-brackets">()
	</span><span class="hl-comment">#we are done with the dialog, destroy it
	</span><span class="hl-identifier">dlg</span><span class="hl-default">.</span><span class="hl-identifier">destroy</span><span class="hl-brackets">()

	</span><span class="hl-comment">#return the result
	</span><span class="hl-reserved">return </span><span class="hl-identifier">name_return</span></pre></div></div>
<p>This function simply shows the dialog and returns the name that the person typed or None if they cancelled.</p>
<p>We will call this function from the on_add_category() function in the PyLan class.  The on_add_category() function is called when you want to add a category to the Tree.  It is called from either the addButton or from a menu item on the menu that the addButton can display.  Here is the function:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_add_category</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the Add Category button is clicked.
	Can also be generally used to add a category</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-identifier">name</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">show_category_dialog</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">name</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-comment"># Get the selection category iter
		</span><span class="hl-identifier">model</span><span class="hl-default">, </span><span class="hl-identifier">selection_iter</span><span class="hl-default">, </span><span class="hl-identifier">todo_cat</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">get_category_selected</span><span class="hl-brackets">()
		</span><span class="hl-comment">#Create the new Category
		</span><span class="hl-identifier">category</span><span class="hl-default"> = </span><span class="hl-identifier">todo</span><span class="hl-default">.</span><span class="hl-identifier">todoCategory</span><span class="hl-brackets">(</span><span class="hl-identifier">name</span><span class="hl-brackets">)
		</span><span class="hl-comment">#Append to the tree
		</span><span class="hl-identifier">category</span><span class="hl-default">.</span><span class="hl-identifier">add_to_tree</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">todoTree</span><span class="hl-code">
			, </span><span class="hl-identifier">selection_iter</span><span class="hl-code">
			, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">__tree_columns</span><span class="hl-brackets">)
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">todo_cat</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">something was selected so add the new category
			to the old category</span><span class="hl-quotes">&quot;&quot;&quot;
			</span><span class="hl-identifier">todo_cat</span><span class="hl-default">.</span><span class="hl-identifier">add_child</span><span class="hl-brackets">(</span><span class="hl-identifier">category</span><span class="hl-brackets">)
		</span><span class="hl-reserved">else</span><span class="hl-default">:
			</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Nothing selected so add to the current project
			data</span><span class="hl-quotes">&quot;&quot;&quot;
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__categories</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">category</span><span class="hl-brackets">)</span></pre></div></div>
<p>The function is relatively simple as it relies on many helper functions.  The first thing that it does is show the category dialog.  If a valid name is returned then we continue adding the category.</p>
<p>We call the get_category_selected() function which is a helper function that is used to get the currently selected category, the <a href="http://www.pygtk.org/docs/pygtk/class-gtktreeiter.html">gtk.TreeIter</a> representing the selection, and the model associated with our gtk.TreeView.  The function wraps the <a href="http://www.pygtk.org/docs/pygtk/class-gtktreeview.html#method-gtktreeview--get-selection">gtk.TreeView.get_selection()</a> and the <a href="http://www.pygtk.org/docs/pygtk/class-gtktreeselection.html#method-gtktreeselection--get-selected">gtk.TreeSelection.get_selected()</a> functions ensuring that todoCategory is selected.  This means that if a todoTask is selected then its parent category will be returned.</p>
<p>We then create the category based on the name and add it to the tree.  If there is a parent category (returned by get_category_selected()) then we add it to its children, otherwise we add it to a new private member self.__categories.  self.__categories is basically a list of all of the top level todoCategories, it is our representation of the data displayed in the tree.  It is created in the __init__ function:</p>
<div class="hl-surround" ><div class="hl-main"><pre>#init todo file
self.todo_file = None
#Get the Main Window
self.main_window = self.wTree.get_widget(&quot;mainWindow&quot;)
self.set_window_title_from_file(self.todo_file)
#No categories
self.__categories = []</pre></div></div>
<p>The get_category_selected() looks like this:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">get_category_selected</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is a wrapper for the
	gtk.TreeView.get_selection() function and the
	gtk.TreeSelection.get_selected() function. It returns
	the same as gtk.TreeSelection.get_selected(), but ensures
	that it is a category that is selected. So if a todoTask
	is selected then it's parent category will be returned.
	@returns A 3-tuple containing a reference to the
	gtk.TreeModel and a gtk.TreeIter pointing to the
	currently selected node. Just like
	gtk.TreeSelection.get_selected but with the todoObject
	being returned as well.
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-comment">#First get the object column
	</span><span class="hl-identifier">todo_ob</span><span class="hl-default"> = </span><span class="hl-reserved">None
	</span><span class="hl-identifier">tcolumn</span><span class="hl-default">, </span><span class="hl-identifier">pos</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">find_todoColumn</span><span class="hl-brackets">(</span><span class="hl-identifier">COL_OBJECT</span><span class="hl-brackets">)
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-reserved">not </span><span class="hl-identifier">tcolumn</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-reserved">return None</span><span class="hl-default">,</span><span class="hl-reserved">None

	</span><span class="hl-comment">#Get the current selection in the gtk.TreeView
	</span><span class="hl-identifier">selection</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">todoTreeView</span><span class="hl-default">.</span><span class="hl-identifier">get_selection</span><span class="hl-brackets">()
	</span><span class="hl-comment"># Get the selection iter
	</span><span class="hl-identifier">model</span><span class="hl-default">, </span><span class="hl-identifier">selection_iter</span><span class="hl-default"> = </span><span class="hl-identifier">selection</span><span class="hl-default">.</span><span class="hl-identifier">get_selected</span><span class="hl-brackets">()
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">selection_iter</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-comment">#Something is selected so get the object
		</span><span class="hl-identifier">todo_ob</span><span class="hl-default"> = </span><span class="hl-identifier">model</span><span class="hl-default">.</span><span class="hl-identifier">get_value</span><span class="hl-brackets">(</span><span class="hl-identifier">selection_iter</span><span class="hl-code">, </span><span class="hl-identifier">tcolumn</span><span class="hl-code">.</span><span class="hl-identifier">pos</span><span class="hl-brackets">)
		</span><span class="hl-identifier">if </span><span class="hl-brackets">((</span><span class="hl-identifier">todo_ob</span><span class="hl-brackets">) </span><span class="hl-identifier">and </span><span class="hl-brackets">(</span><span class="hl-identifier">todo_ob</span><span class="hl-code">.</span><span class="hl-identifier">type</span><span class="hl-code"> != </span><span class="hl-identifier">todo</span><span class="hl-code">.</span><span class="hl-identifier">CATEGORY</span><span class="hl-brackets">))</span><span class="hl-default">:
			</span><span class="hl-comment">#Alright we need the parent, this is not a category
			</span><span class="hl-identifier">selection_iter</span><span class="hl-default"> = </span><span class="hl-identifier">model</span><span class="hl-default">.</span><span class="hl-identifier">iter_parent</span><span class="hl-brackets">(</span><span class="hl-identifier">selection_iter</span><span class="hl-brackets">)
			</span><span class="hl-identifier">todo_ob</span><span class="hl-default"> = </span><span class="hl-identifier">model</span><span class="hl-default">.</span><span class="hl-identifier">get_value</span><span class="hl-brackets">(</span><span class="hl-identifier">selection_iter</span><span class="hl-code">, </span><span class="hl-identifier">tcolumn</span><span class="hl-code">.</span><span class="hl-identifier">pos</span><span class="hl-brackets">)
	</span><span class="hl-reserved">return </span><span class="hl-identifier">model</span><span class="hl-default">, </span><span class="hl-identifier">selection_iter</span><span class="hl-default">, </span><span class="hl-identifier">todo_ob</span></pre></div></div>
<p>It relies on another helper function find_todoColumn which basically loops through the self.__tree_columns list looking for the todoColumn with the matching ID.  If is found it is returned, along with the position in the list that it was returned at.  This is partly done because I am unsure whether the order in the list will matter after initialization or whether the objects in the list should keep track of their position.  If it is the objects, then they could be moved into a dictionary later on (you&#8217;ll notice that one is already created) for faster access.</p>
<p>We use find_todoColum() so that we can get the column representing the Object. (We use the Object instead of the type since we may need to get the actual object later.  If we just wanted the type we could have used COL_OBJECT_TYPE)</p>
<p>Then what we do is we get the current gtk.TreeSelection and then get what (if anything) is actually selected within it.  If something is selected (selection_iter is not None) then we check to see if the selected item is a category or not.  If it is not a category we get the parent of the selecton iter, which we know is a category since tasks cannot have children.</p>
<p>Then we simply return all of the values: the current model, the gtk.TreeIter representing the selection category (may be None) and the todoCategory object (may also be none).</p>
<p>find_todoColumn() should be self explanatory:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">find_todoColumn</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">column_ID</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is used to search the __tree_columns
	list to find a specific todoColumn.
	@param column_ID - The ID of the column that we
	are looking for.
	@returns todoColumn, int - The column found and
	the position in the list. If todoColumn is None
	then the column was not found.
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">count</span><span class="hl-default"> = </span><span class="hl-number">0
	</span><span class="hl-identifier">columnReturn</span><span class="hl-default"> = </span><span class="hl-reserved">None
	for </span><span class="hl-identifier">item_column </span><span class="hl-reserved">in </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__tree_columns</span><span class="hl-default">:
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">item_column</span><span class="hl-code">.</span><span class="hl-identifier">ID</span><span class="hl-code"> == </span><span class="hl-identifier">column_ID</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-identifier">columnReturn</span><span class="hl-default"> = </span><span class="hl-identifier">item_column
			</span><span class="hl-reserved">break
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">columnReturn</span><span class="hl-code"> == </span><span class="hl-reserved">None</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-comment">#Something is really wrong we did not match
		</span><span class="hl-identifier">helper</span><span class="hl-default">.</span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Error column data appears corrupted</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">))
	</span><span class="hl-comment">#Return the results
	</span><span class="hl-reserved">return </span><span class="hl-identifier">columnReturn</span><span class="hl-default">, </span><span class="hl-identifier">count</span></pre></div></div>
<p>After all that code, we are finally able to add things to our tree!</p>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/pylan_01/pylan_01_05.png" alt="Python GTD pyGTK" border="0" /></p>
<p>It may seem complicated or like a lot of work, but in reality it&#8217;s simply a large process broken up into smaller reusable bits that make more sense when looked at as a whole.</p>
<h2><a name="RemovingCategories">The Code: Removing Categories</a></h2>
<p>Now that we can add categories let&#8217;s add some code so that we can remove them.  We will do this in the on_remove_item() function which is triggered by the btnRemoveCategory button&#8217;s clicked signal:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_remove_item</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">called then the remove category button is clicked.
	Can also be generally used to remove a category or
	item.</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-comment">#Get the column of the object
	</span><span class="hl-identifier">tcolumn</span><span class="hl-default">, </span><span class="hl-identifier">pos</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">find_todoColumn</span><span class="hl-brackets">(</span><span class="hl-identifier">COL_OBJECT</span><span class="hl-brackets">)
	</span><span class="hl-comment">#Get the current selection in the gtk.TreeView
	</span><span class="hl-identifier">selection</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">todoTreeView</span><span class="hl-default">.</span><span class="hl-identifier">get_selection</span><span class="hl-brackets">()
	</span><span class="hl-comment"># Get the selection iter
	</span><span class="hl-identifier">model</span><span class="hl-default">, </span><span class="hl-identifier">selection_iter</span><span class="hl-default"> = </span><span class="hl-identifier">selection</span><span class="hl-default">.</span><span class="hl-identifier">get_selected</span><span class="hl-brackets">()
	</span><span class="hl-identifier">if </span><span class="hl-brackets">((</span><span class="hl-identifier">selection_iter</span><span class="hl-brackets">) </span><span class="hl-identifier">and </span><span class="hl-brackets">(</span><span class="hl-identifier">tcolumn</span><span class="hl-brackets">))</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">All right we have something to remove and we have the
		column that represents the todo object, first
		let's get the object from the selection_iter</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">todo_ob</span><span class="hl-default"> = </span><span class="hl-identifier">model</span><span class="hl-default">.</span><span class="hl-identifier">get_value</span><span class="hl-brackets">(</span><span class="hl-identifier">selection_iter</span><span class="hl-code">, </span><span class="hl-identifier">tcolumn</span><span class="hl-code">.</span><span class="hl-identifier">pos</span><span class="hl-brackets">)
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">todo_ob</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Allright everything worked, time to remove. Does
			this item have a parent</span><span class="hl-quotes">&quot;&quot;&quot;
			</span><span class="hl-identifier">todo_parent</span><span class="hl-default">, </span><span class="hl-identifier">iter</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">get_parent_category</span><span class="hl-brackets">(
				</span><span class="hl-identifier">selection_iter</span><span class="hl-code">, </span><span class="hl-identifier">tcolumn</span><span class="hl-code">.</span><span class="hl-identifier">pos</span><span class="hl-brackets">)
			</span><span class="hl-comment">#Remove from the tree
			</span><span class="hl-identifier">model</span><span class="hl-default">.</span><span class="hl-identifier">remove</span><span class="hl-brackets">(</span><span class="hl-identifier">selection_iter</span><span class="hl-brackets">)
			</span><span class="hl-comment">#if there is a parent, remove from the parent
			</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">todo_parent</span><span class="hl-brackets">)</span><span class="hl-default">:
				</span><span class="hl-identifier">todo_parent</span><span class="hl-default">.</span><span class="hl-identifier">remove_child</span><span class="hl-brackets">(</span><span class="hl-identifier">todo_ob</span><span class="hl-brackets">)
			</span><span class="hl-reserved">else</span><span class="hl-default">:
				</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">There is no parent so remove from
				base list</span><span class="hl-quotes">&quot;&quot;&quot;
				</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__categories</span><span class="hl-default">.</span><span class="hl-identifier">remove</span><span class="hl-brackets">(</span><span class="hl-identifier">todo_ob</span><span class="hl-brackets">)</span></pre></div></div>
<p>If you understood how we added the columns, how we remove them should be pretty straight forward.  First we get the object todoColumn and the gtk.TreeSelection.  Then if they are both valid we get the object that is selected.  If nothing is selected then we can return because there is nothing to remove.</p>
<p>If something is selected then we call the helper get_parent_category() which simply gets us the item&#8217;s parent category.  We then remove the selected item from the tree, but since the object is still stored within our data tree (self.__categories) we need to remove it from there.  </p>
<p>If the item has a parent (todo_parent) we remove the item from the parent by calling remove_child().  If the item did not have a parent it means that it is a top level category and needs to be removed directly from self.__categories.</p>
<p>Here is get_parent_category():</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">get_parent_category</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">selection_iter</span><span class="hl-code">, </span><span class="hl-identifier">object_pos</span><span class="hl-code"> = </span><span class="hl-reserved">None</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Get the parent category of the selection_item
	@param selection_iter - gtk.TreeIter - A iter whose
	parent you want to get.
	@param object_pos - number - The index of the object
	column.  If None this will be calculated.
	@returns - todoCategory, gtk.TreeIter - The parent category of the
	iter on success, or None on failure. The parent gtk.TreeIter
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">object_pos</span><span class="hl-code"> == </span><span class="hl-reserved">None</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">tcolumn</span><span class="hl-default">, </span><span class="hl-identifier">pos</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">find_todoColumn</span><span class="hl-brackets">(</span><span class="hl-identifier">COL_OBJECT</span><span class="hl-brackets">)
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">tcolumn</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-identifier">object_pos</span><span class="hl-default"> = </span><span class="hl-identifier">tcolumn</span><span class="hl-default">.</span><span class="hl-identifier">pos
		</span><span class="hl-reserved">else</span><span class="hl-default">:
			</span><span class="hl-comment">#Column Data Error
			</span><span class="hl-reserved">return None</span><span class="hl-default">, </span><span class="hl-reserved">None
			
	</span><span class="hl-identifier">todo_parent</span><span class="hl-default"> = </span><span class="hl-reserved">None
	</span><span class="hl-identifier">selection_parent</span><span class="hl-default"> = </span><span class="hl-reserved">None
	</span><span class="hl-comment">#Get the Model
	</span><span class="hl-identifier">model</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">todoTreeView</span><span class="hl-default">.</span><span class="hl-identifier">get_model</span><span class="hl-brackets">()
	</span><span class="hl-comment">#Get the current selection in the gtk.TreeView
	</span><span class="hl-identifier">selection</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">todoTreeView</span><span class="hl-default">.</span><span class="hl-identifier">get_selection</span><span class="hl-brackets">()
	</span><span class="hl-identifier">if </span><span class="hl-brackets">((</span><span class="hl-identifier">model</span><span class="hl-brackets">) </span><span class="hl-identifier">and </span><span class="hl-brackets">(</span><span class="hl-identifier">selection</span><span class="hl-brackets">))</span><span class="hl-default">:
		</span><span class="hl-identifier">selection_parent</span><span class="hl-default"> = </span><span class="hl-identifier">model</span><span class="hl-default">.</span><span class="hl-identifier">iter_parent</span><span class="hl-brackets">(</span><span class="hl-identifier">selection_iter</span><span class="hl-brackets">)
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">selection_parent</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-identifier">todo_parent</span><span class="hl-default"> = </span><span class="hl-identifier">model</span><span class="hl-default">.</span><span class="hl-identifier">get_value</span><span class="hl-brackets">(</span><span class="hl-identifier">selection_parent</span><span class="hl-code">, </span><span class="hl-identifier">object_pos</span><span class="hl-brackets">)

	</span><span class="hl-reserved">return </span><span class="hl-identifier">todo_parent</span><span class="hl-default">, </span><span class="hl-identifier">selection_parent</span></pre></div></div>
<h2><a name="SavingAndLoadingWithcPickle">The Code: Saving and Loading with cPickle</a></h2>
<p>Now that we can add and remove categories, the next thing that we want to do is be able to save and load our data to and from files.  This code is very similar to the method used in my <a href="http://www.learningpython.com/2006/09/02/extending-our-pygtk-application/">Extending our PyGTK Application</a> so I will gloss over the repeated code and only explain the new stuff.</p>
<p>You probably remember the helper module mention above and used a little bit in some of the previously mentioned code.  It&#8217;s simply a helper module that I have started to use so that I don&#8217;t always have to recode the same functions.  The file&#8217;s name is &#8220;helper.py&#8221; and it looks like this:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-comment">#!/usr/bin/env python

</span><span class="hl-reserved">try</span><span class="hl-default">:
 	</span><span class="hl-reserved">import </span><span class="hl-identifier">pygtk
  	pygtk</span><span class="hl-default">.</span><span class="hl-identifier">require</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">2.0</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
</span><span class="hl-reserved">except</span><span class="hl-default">:
  	</span><span class="hl-reserved">pass
try</span><span class="hl-default">:
	</span><span class="hl-reserved">import </span><span class="hl-identifier">os
	</span><span class="hl-reserved">import </span><span class="hl-identifier">gtk
</span><span class="hl-reserved">except ImportError</span><span class="hl-default">, </span><span class="hl-identifier">e</span><span class="hl-default">:
	</span><span class="hl-reserved">print </span><span class="hl-quotes">&quot;</span><span class="hl-string">Import error in helper:</span><span class="hl-quotes">&quot;</span><span class="hl-default">, </span><span class="hl-identifier">e
	sys</span><span class="hl-default">.</span><span class="hl-identifier">exit</span><span class="hl-brackets">(</span><span class="hl-number">1</span><span class="hl-brackets">)

</span><span class="hl-reserved">def </span><span class="hl-identifier">file_browse</span><span class="hl-brackets">(</span><span class="hl-identifier">dialog_action</span><span class="hl-code">, </span><span class="hl-identifier">filters</span><span class="hl-code">, </span><span class="hl-identifier">file_extension</span><span class="hl-code">=</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-code">, </span><span class="hl-identifier">file_name</span><span class="hl-code">=</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is used to browse for a file.
	It can be either a save or open dialog depending on
	what dialog_action is.
	The path to the file will be returned if the user
	selects one, however a blank string will be returned
	if they cancel or do not select one.
	dialog_action - The open or save mode for the dialog either
	gtk.FILE_CHOOSER_ACTION_OPEN, gtk.FILE_CHOOSER_ACTION_SAVE
	@param filters - list - list of  gtk.FileFilter() objects
	that will be added to the dialog.
	@param file_extension - string - The file extension that will be
	added to the filename when saving
	@param file_name - Default name when doing a save
	@returns - File Name, or None on cancel.
	</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">dialog_action</span><span class="hl-code">==</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_OPEN</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">dialog_buttons</span><span class="hl-default"> = </span><span class="hl-brackets">(</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">STOCK_CANCEL</span><span class="hl-code">
			, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_CANCEL</span><span class="hl-code">
			, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">STOCK_OPEN</span><span class="hl-code">
			, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_OK</span><span class="hl-brackets">)
		</span><span class="hl-identifier">dlg_title</span><span class="hl-default"> = </span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Open File</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-reserved">else</span><span class="hl-default">:
		</span><span class="hl-identifier">dialog_buttons</span><span class="hl-default"> = </span><span class="hl-brackets">(</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">STOCK_CANCEL</span><span class="hl-code">
			, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_CANCEL</span><span class="hl-code">
			, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">STOCK_SAVE</span><span class="hl-code">
			, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_OK</span><span class="hl-brackets">)
		</span><span class="hl-identifier">dlg_title</span><span class="hl-default"> = </span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Save File</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)

	</span><span class="hl-identifier">file_dialog</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">FileChooserDialog</span><span class="hl-brackets">(</span><span class="hl-identifier">title</span><span class="hl-code">=</span><span class="hl-identifier">dlg_title</span><span class="hl-code">
		, </span><span class="hl-identifier">action</span><span class="hl-code">=</span><span class="hl-identifier">dialog_action</span><span class="hl-code">
		, </span><span class="hl-identifier">buttons</span><span class="hl-code">=</span><span class="hl-identifier">dialog_buttons</span><span class="hl-brackets">)
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">set the filename if we are saving</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">dialog_action</span><span class="hl-code">==</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_SAVE</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">set_current_name</span><span class="hl-brackets">(</span><span class="hl-identifier">file_name</span><span class="hl-brackets">)
	</span><span class="hl-comment">#Add filters
	</span><span class="hl-reserved">for </span><span class="hl-identifier">filter </span><span class="hl-reserved">in </span><span class="hl-identifier">filters</span><span class="hl-default">:
		</span><span class="hl-identifier">file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">add_filter</span><span class="hl-brackets">(</span><span class="hl-identifier">filter</span><span class="hl-brackets">)
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">dialog_action</span><span class="hl-code">==</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_OPEN</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Create and add the 'all files' filter</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">filter</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">FileFilter</span><span class="hl-brackets">()
		</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">set_name</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">All files</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">))
		</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">add_pattern</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">*</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-identifier">file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">add_filter</span><span class="hl-brackets">(</span><span class="hl-identifier">filter</span><span class="hl-brackets">)

	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Init the return value</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">result</span><span class="hl-default"> = </span><span class="hl-reserved">None
	if </span><span class="hl-identifier">file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">run</span><span class="hl-brackets">()</span><span class="hl-default"> == </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">RESPONSE_OK</span><span class="hl-default">:
		</span><span class="hl-identifier">result</span><span class="hl-default"> = </span><span class="hl-identifier">file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">get_filename</span><span class="hl-brackets">()
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">dialog_action</span><span class="hl-code">==</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_SAVE</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-identifier">result</span><span class="hl-default">, </span><span class="hl-identifier">extension</span><span class="hl-default"> = </span><span class="hl-identifier">os</span><span class="hl-default">.</span><span class="hl-identifier">path</span><span class="hl-default">.</span><span class="hl-identifier">splitext</span><span class="hl-brackets">(</span><span class="hl-identifier">result</span><span class="hl-brackets">)
			</span><span class="hl-identifier">result</span><span class="hl-default"> = </span><span class="hl-identifier">result</span><span class="hl-default"> + </span><span class="hl-quotes">&quot;</span><span class="hl-string">.</span><span class="hl-quotes">&quot;</span><span class="hl-default"> + </span><span class="hl-identifier">file_extension
	file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">destroy</span><span class="hl-brackets">()

	</span><span class="hl-reserved">return </span><span class="hl-identifier">result

</span><span class="hl-reserved">def </span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(</span><span class="hl-identifier">error_string</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This Function is used to show an error dialog when
	an error occurs.
	error_string - The error string that will be displayed
	on the dialog.
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">error_dlg</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">MessageDialog</span><span class="hl-brackets">(</span><span class="hl-identifier">type</span><span class="hl-code">=</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">MESSAGE_ERROR</span><span class="hl-code">
				, </span><span class="hl-identifier">message_format</span><span class="hl-code">=</span><span class="hl-identifier">error_string</span><span class="hl-code">
				, </span><span class="hl-identifier">buttons</span><span class="hl-code">=</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">BUTTONS_OK</span><span class="hl-brackets">)
	</span><span class="hl-identifier">error_dlg</span><span class="hl-default">.</span><span class="hl-identifier">run</span><span class="hl-brackets">()
	</span><span class="hl-identifier">error_dlg</span><span class="hl-default">.</span><span class="hl-identifier">destroy</span><span class="hl-brackets">()</span></pre></div></div>
<p>These are both old functions (changed slightly) so I won&#8217;t go over them.</p>
<p>The same is true for the File menu signal handers:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_file_new</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">File | New - Start a new project file, blank out
	the currnet project and start from scratch</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__categories</span><span class="hl-default"> = </span><span class="hl-brackets">[]
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">todo_file</span><span class="hl-default"> = </span><span class="hl-reserved">None
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">set_window_title_from_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">todo_file</span><span class="hl-brackets">)
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">reload_from_data</span><span class="hl-brackets">()


</span><span class="hl-reserved">def </span><span class="hl-identifier">on_file_open</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Function called to open a todo file</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">todo_file</span><span class="hl-default"> = </span><span class="hl-identifier">helper</span><span class="hl-default">.</span><span class="hl-identifier">file_browse</span><span class="hl-brackets">(</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_OPEN</span><span class="hl-code">
		, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">get_browse_filter_list</span><span class="hl-brackets">()</span><span class="hl-code">
		, </span><span class="hl-identifier">FILE_EXT</span><span class="hl-brackets">)
	</span><span class="hl-comment">#If we have a file
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">todo_file</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">load_from_file</span><span class="hl-brackets">(</span><span class="hl-identifier">todo_file</span><span class="hl-brackets">))</span><span class="hl-default">:
			</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Allright it all worked! save the current file and
			set the title.</span><span class="hl-quotes">&quot;&quot;&quot;
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">todo_file</span><span class="hl-default"> = </span><span class="hl-identifier">todo_file
			self</span><span class="hl-default">.</span><span class="hl-identifier">set_window_title_from_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">todo_file</span><span class="hl-brackets">)

</span><span class="hl-reserved">def </span><span class="hl-identifier">on_file_save</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">File | Save function - Save the Todo file</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-comment"># Let the user browse for the save location and name
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">todo_file</span><span class="hl-code"> == </span><span class="hl-reserved">None</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">todo_file</span><span class="hl-default"> = </span><span class="hl-identifier">helper</span><span class="hl-default">.</span><span class="hl-identifier">file_browse</span><span class="hl-brackets">(</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_SAVE</span><span class="hl-code">
		, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">get_browse_filter_list</span><span class="hl-brackets">()</span><span class="hl-code">
		, </span><span class="hl-identifier">FILE_EXT</span><span class="hl-brackets">)
	</span><span class="hl-comment">#If we have a todo file
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">todo_file</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">save_to_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">todo_file</span><span class="hl-brackets">))</span><span class="hl-default">:
			</span><span class="hl-comment">#Allright it all worked! Set the Title
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">set_window_title_from_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">todo_file</span><span class="hl-brackets">)

</span><span class="hl-reserved">def </span><span class="hl-identifier">on_file_save_as</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">File | Save As function - Save the todo file</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">todo_file</span><span class="hl-default"> = </span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Untitled</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">todo_file</span><span class="hl-code"> != </span><span class="hl-reserved">None</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">todo_file</span><span class="hl-default"> = </span><span class="hl-identifier">os</span><span class="hl-default">.</span><span class="hl-identifier">path</span><span class="hl-default">.</span><span class="hl-identifier">basename</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">todo_file</span><span class="hl-brackets">)

	</span><span class="hl-identifier">todo_file</span><span class="hl-default"> = </span><span class="hl-identifier">helper</span><span class="hl-default">.</span><span class="hl-identifier">file_browse</span><span class="hl-brackets">(</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_SAVE</span><span class="hl-code">
		, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">get_browse_filter_list</span><span class="hl-brackets">()</span><span class="hl-code">
		, </span><span class="hl-identifier">FILE_EXT</span><span class="hl-brackets">)
	</span><span class="hl-comment">#If we have a xml_file
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">todo_file</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">save_to_file</span><span class="hl-brackets">(</span><span class="hl-identifier">todo_file</span><span class="hl-brackets">))</span><span class="hl-default">:
			</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Allright it all worked! save the current file and
			set the title.</span><span class="hl-quotes">&quot;&quot;&quot;
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">todo_file</span><span class="hl-default"> = </span><span class="hl-identifier">todo_file
			self</span><span class="hl-default">.</span><span class="hl-identifier">set_window_title_from_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">todo_file</span><span class="hl-brackets">)</span></pre></div></div>
<p>They are all taken almost verbatim from previous tutorials so I won&#8217;t explaining them in detail.  The do rely on one helper function: get_browse_filter_list() that simply creates a list of gtk.FileFilters to use when browsing:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">get_browse_filter_list</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Used to get the list of gtk.FileFilter objects
	to use when browsing for a file.
	@returns - list - List of gtk.FileFilter objects
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">filter</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">FileFilter</span><span class="hl-brackets">()
	</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">set_name</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Todo file</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">))
	</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">add_pattern</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">*.</span><span class="hl-quotes">&quot;</span><span class="hl-code"> + </span><span class="hl-identifier">FILE_EXT</span><span class="hl-brackets">)
	</span><span class="hl-reserved">return </span><span class="hl-brackets">[</span><span class="hl-identifier">filter</span><span class="hl-brackets">]</span></pre></div></div>
<p>The actual work of saving and loading the data happens in the save_to_file(), load_from_file(), and reload_from_data() functions.  We&#8217;ll go over the save_to_file() function first:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">save_to_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">filename</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Save the current todoproject to a filename
		@param filename - string - the file name to save
		the file too.
		@returns boolean - success or failure
		</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-reserved">try</span><span class="hl-default">:
			</span><span class="hl-identifier">todo_file</span><span class="hl-default"> = </span><span class="hl-builtin">open</span><span class="hl-brackets">(</span><span class="hl-identifier">filename</span><span class="hl-code">, </span><span class="hl-quotes">&quot;</span><span class="hl-string">w</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
			</span><span class="hl-identifier">cPickle</span><span class="hl-default">.</span><span class="hl-identifier">dump</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">__categories</span><span class="hl-code">, </span><span class="hl-identifier">todo_file</span><span class="hl-code">, </span><span class="hl-identifier">cPickle</span><span class="hl-code">.</span><span class="hl-identifier">HIGHEST_PROTOCOL</span><span class="hl-brackets">)
			</span><span class="hl-identifier">todo_file</span><span class="hl-default">.</span><span class="hl-identifier">close</span><span class="hl-brackets">()
			</span><span class="hl-reserved">return True
		except </span><span class="hl-identifier">cPickle</span><span class="hl-default">.</span><span class="hl-identifier">PicklingError</span><span class="hl-default">, </span><span class="hl-identifier">e</span><span class="hl-default">:
			</span><span class="hl-identifier">helper</span><span class="hl-default">.</span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Error saving file: %s</span><span class="hl-special">\r\n</span><span class="hl-string">%s</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code"> % </span><span class="hl-brackets">(</span><span class="hl-identifier">filename</span><span class="hl-code">, </span><span class="hl-identifier">e</span><span class="hl-brackets">))
			</span><span class="hl-reserved">return False
		except</span><span class="hl-default">:
			</span><span class="hl-identifier">helper</span><span class="hl-default">.</span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Error saving file: %s</span><span class="hl-quotes">&quot;</span><span class="hl-code"> % </span><span class="hl-identifier">filename</span><span class="hl-brackets">))
			</span><span class="hl-reserved">return False</span></pre></div></div>
<p>Besides the exception handling, the actual code in this function is very simple:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-identifier">todo_file</span><span class="hl-default"> = </span><span class="hl-builtin">open</span><span class="hl-brackets">(</span><span class="hl-identifier">filename</span><span class="hl-code">, </span><span class="hl-quotes">&quot;</span><span class="hl-string">w</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
</span><span class="hl-identifier">cPickle</span><span class="hl-default">.</span><span class="hl-identifier">dump</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">__categories</span><span class="hl-code">, </span><span class="hl-identifier">todo_file</span><span class="hl-code">, </span><span class="hl-identifier">cPickle</span><span class="hl-code">.</span><span class="hl-identifier">HIGHEST_PROTOCOL</span><span class="hl-brackets">)
</span><span class="hl-identifier">todo_file</span><span class="hl-default">.</span><span class="hl-identifier">close</span><span class="hl-brackets">()
</span><span class="hl-reserved">return True</span></pre></div></div>
<p>First we open the file for writing, then we use <a href="http://docs.python.org/lib/module-cPickle.html">cPickle</a> to dump our __categories tree into the file, and then we close the file.  That&#8217;s it. </p>
<p>Loading the data is equally as simple:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">load_from_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">filename</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Try to a todo project from a
	specific file.
	@param filename - string - the file name to load
	from
	@returns boolean - success or failure
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-reserved">try</span><span class="hl-default">:
		</span><span class="hl-identifier">todo_file</span><span class="hl-default"> = </span><span class="hl-builtin">open</span><span class="hl-brackets">(</span><span class="hl-identifier">filename</span><span class="hl-code">, </span><span class="hl-quotes">&quot;</span><span class="hl-string">rb</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__categories</span><span class="hl-default"> = </span><span class="hl-identifier">cPickle</span><span class="hl-default">.</span><span class="hl-identifier">load</span><span class="hl-brackets">(</span><span class="hl-identifier">todo_file</span><span class="hl-brackets">)
		</span><span class="hl-identifier">todo_file</span><span class="hl-default">.</span><span class="hl-identifier">close</span><span class="hl-brackets">()
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">reload_from_data</span><span class="hl-brackets">()
		</span><span class="hl-reserved">return True
	except </span><span class="hl-identifier">cPickle</span><span class="hl-default">.</span><span class="hl-identifier">UnpicklingError</span><span class="hl-default">, </span><span class="hl-identifier">e</span><span class="hl-default">:
		</span><span class="hl-identifier">helper</span><span class="hl-default">.</span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Error opening file: %s</span><span class="hl-special">\r\n</span><span class="hl-string">%s</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code"> % </span><span class="hl-brackets">(</span><span class="hl-identifier">filename</span><span class="hl-code">, </span><span class="hl-identifier">e</span><span class="hl-brackets">))
		</span><span class="hl-reserved">return False
	except</span><span class="hl-default">:
		</span><span class="hl-identifier">helper</span><span class="hl-default">.</span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Error opening file: %s</span><span class="hl-quotes">&quot;</span><span class="hl-code"> % </span><span class="hl-identifier">filename</span><span class="hl-brackets">))
		</span><span class="hl-reserved">return False</span></pre></div></div>
<p>First we open the file for reading and in binary mode (as instructed by the <a href="http://docs.python.org/lib/pickle-example.html">Pickle documentation</a>) and then we load the file into our self.__categories tree.  After that we close the files and reload the tree from self.__categories using the reload_from_data() function.</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">reload_from_data</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when we want to reset everything based
	on internal data.  Probably called when a file has
	been loaded.</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">todoTree</span><span class="hl-default">.</span><span class="hl-identifier">clear</span><span class="hl-brackets">()
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">__categories</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-reserved">for </span><span class="hl-identifier">todo_ob </span><span class="hl-reserved">in </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__categories</span><span class="hl-default">:
			</span><span class="hl-identifier">todo_ob</span><span class="hl-default">.</span><span class="hl-identifier">add_to_tree</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">todoTree</span><span class="hl-code">, </span><span class="hl-reserved">None</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">__tree_columns</span><span class="hl-brackets">)
	</span><span class="hl-reserved">else</span><span class="hl-default">:
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">__categories</span><span class="hl-default"> = </span><span class="hl-brackets">[]</span></pre></div></div>
<p>reload_from_data simply clears the gtk.TreeStore using the <a href="http://www.pygtk.org/docs/pygtk/class-gtktreestore.html#method-gtktreestore--clear">gtk.TreeStore.clear()</a> function.  Then it loops through all of the toplevel categories in self.__categories and adds each category to the tree by calling the add_to_tree() function.  If self.__categories is None for some reason (perhaps a bad file?) we simply reset it to a blank list.</p>
<p>If you remember the add_to_tree() function you&#8217;ll remember that it will also add all children of the category (and their children recursively) to the tree.</p>
<h2><a name="ConnectingOurMenuWithThegtkMenuToolButton">The Code: Connecting our menu with the gtk.MenuToolButton</a></h2>
<p>Well we&#8217;re almost done here, all we have to do is connect our gtk.MenuToolButton with the menu that we created way back in the GUI section.  Up until now you may have noticed that the arrow beside the Add button has been greyed out or disabled, this is because we have not attached a menu to the button yet using the <a href="http://www.pygtk.org/docs/pygtk/class-gtkmenutoolbutton.html#method-gtkmenutoolbutton--set-menu">gtk.MenuToolButton.set_menu()</a> function.  We will connect the menu to the button in a function called initialize_menus() that will be called in the __init__ function:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-comment">#Initialize the todo Tree
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">initialize_todo_tree</span><span class="hl-brackets">()
</span><span class="hl-comment">#initialize menus
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">initialize_menus</span><span class="hl-brackets">()</span></pre></div></div>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">initialize_menus</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called to initialize the menus</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-identifier">add_button</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">btnAdd</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-comment">#load the menu from the glade file
	</span><span class="hl-identifier">wTree</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">glade</span><span class="hl-default">.</span><span class="hl-identifier">XML</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">gladefile</span><span class="hl-code">, </span><span class="hl-quotes">&quot;</span><span class="hl-string">addMenu</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-comment">#connect the menu with the signals
	</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">signal_autoconnect</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)
	</span><span class="hl-comment">#Get the menu dialog widget
	</span><span class="hl-identifier">menu</span><span class="hl-default"> = </span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">addMenu</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">if </span><span class="hl-brackets">((</span><span class="hl-identifier">add_button</span><span class="hl-brackets">) </span><span class="hl-identifier">and </span><span class="hl-brackets">(</span><span class="hl-identifier">menu</span><span class="hl-brackets">))</span><span class="hl-default">:
		</span><span class="hl-identifier">add_button</span><span class="hl-default">.</span><span class="hl-identifier">set_menu</span><span class="hl-brackets">(</span><span class="hl-identifier">menu</span><span class="hl-brackets">)</span></pre></div></div>
<p>The first thing that we do is get the our gtk.MenuToolButton &#8220;btnAdd&#8221; from the widget tree.  Then we get the widget tree that represents the &#8220;addMenu&#8221;.  We then auto connect that widget tree with ourselves (this connects the &#8220;Add Category&#8221; menu item with our on_add_category() function.  Then we get the actual <a href="http://www.pygtk.org/docs/pygtk/class-gtkmenu.html">gtk.Menu</a> from the menu&#8217;s widget tree.</p>
<p>If all of the widgets have been returned properly we then connect the two using the <a href="http://www.pygtk.org/docs/pygtk/class-gtkmenutoolbutton.html#method-gtkmenutoolbutton--set-menu">gtk.MenuToolButton.set_menu()</a> function.</p>
<p>Now when you run the application you will be able to use the menu to add categories!</p>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/pylan_01/pylan_01_06.png" alt="Python GTD pyGTK" border="0" /></p>
<h2><a name="Conclusion">Conclusion</a></h2>
<p>You can download the full source to this tutorial <a href="http://www.learningpython.com/sources/PyLan_01.tar.gz">here</a>.</p>
<p>Whew!  That was a lot of code and a lot of text to get through.  Hopefully you were able to understand and follow along with all of it, and hopefully the next parts won&#8217;t be as long!  The only reason that this part was so long was that it had so much ground to cover and because so much of it had been covered before.  Future tutorials should cover smaller and more discrete tasks.</p>
<p>You will also notice that none of the translation files are included with the source, this was simply left out for now since there will be more text added in the future.  There are also some extra dialogs in the glade project that will be used in future tutorials, if you want you could start implementing them.</p>
<p>Of course you will probably never find yourself creating an application that is identical to this pyLan application, but the idea is that you may find yourself creating an application that is <i>similar</i> or that requires <i>similar elements</i>.  In those situations hopefully you will be able to re-use or re-factor that code found in this, and future, tutorials.</p>
<p>As always if you find any errors or have any questions feel free to leave a comment.  If however you have a general question about python or PyGTK I would appreciate it if you ask it in the <a href="http://www.learningpython.com/forums/">LearningPython</a> forums so that a possible solution to someone elses problem will not get buried in an unrelated area.</p>
<p>Time for a some food!</todocategory></todocolumn></selsine></p>
<div style="float:right;margin:0px 0px 0px 0px;"><a href="http://www.google.com/reader/link?url=http://www.learningpython.com/2007/02/17/pylan-a-gtd-todo-application-written-in-python-and-pygtk-part-one/&title=PyLan a GTD todo application written in python and PyGTK - part one&srcTitle=learning python&srcURL=http://www.learningpython.com"target="_blank" rel=""><img border="0" src="http://www.learningpython.com/wp-content/plugins/wp-google-buzz/icon/12.png" style="opacity:1;filter:alpha(opacity=100)" onmouseover="this.style.opacity=0.8;this.filters.alpha.opacity=70" onmouseout="this.style.opacity=1;this.filters.alpha.opacity=100"/> </a></div>]]></content:encoded>
			<wfw:commentRss>http://www.learningpython.com/2007/02/17/pylan-a-gtd-todo-application-written-in-python-and-pygtk-part-one/feed/</wfw:commentRss>
		<slash:comments>20</slash:comments>
		</item>
		<item>
		<title>Translating your Python/PyGTK application</title>
		<link>http://www.learningpython.com/2006/12/03/translating-your-pythonpygtk-application/</link>
		<comments>http://www.learningpython.com/2006/12/03/translating-your-pythonpygtk-application/#comments</comments>
		<pubDate>Sun, 03 Dec 2006 21:19:55 +0000</pubDate>
		<dc:creator>selsine</dc:creator>
				<category><![CDATA[PyGTK]]></category>
		<category><![CDATA[glade]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.learningpython.com/?p=48</guid>
		<description><![CDATA[
			
				
			
		
In this tutorial we will go over the (very) basic steps that you can use in order to translate (localize or internationalize) your PyGTK application.  For this tutorial we are going to use the PyWine application that we have been working with in two previous tutorials:

Building an Application with PyGTK and Glade
Extending our PyGTK [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.learningpython.com%2F2006%2F12%2F03%2Ftranslating-your-pythonpygtk-application%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.learningpython.com%2F2006%2F12%2F03%2Ftranslating-your-pythonpygtk-application%2F&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<p>In this tutorial we will go over the (very) basic steps that you can use in order to translate (localize or internationalize) your PyGTK application.  For this tutorial we are going to use the PyWine application that we have been working with in two previous tutorials:</p>
<ul>
<li><a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">Building an Application with PyGTK and Glade</a></li>
<li><a href="http://www.learningpython.com/2006/09/02/extending-our-pygtk-application/">Extending our PyGTK Application</a></li>
</ul>
<p>The full source and necessary files for this tutorial can be downloaded <a href="http://www.learningpython.com/sources/pywine_03.tar.gz">here</a>.</p>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/pywine_03/pywine_03_01png" alt="Python translation" border="0" /></p>
<h2>Creating our Portable Object Files</h2>
<p>In order to translate our text we are going to use the <a href="http://docs.python.org/lib/module-gettext.html">gettext module</a>.  The gettext module is basically a wrapper for the GNU gettext utility whose documentation can be found <a href="http://www.gnu.org/software/gettext/manual/gettext.html">here</a>.</p>
<p>The first step is to edit our python file (pywine.py)and &#8220;mark&#8221; all of the strings that we want to translate in our python code.  To do this we will use the _(&#8221;xxx&#8221;) standard that most people use to mark strings that need to be translated.  Otherwise in situations like the following:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-comment">#Get the treeView from the widget Tree
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wineView</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">get_widget</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">wineView</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span></pre></div></div>
<p>&#8220;wineView&#8221; might be thought of as a string to be translated when it clearly should not be.  So we need to go through the .py file and wrap each of my English strings in _().  Here are all of the strings that need to be marked in their marked form:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">sWine</span><span class="hl-default"> = </span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Wine</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">sWinery</span><span class="hl-default"> = </span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Winery</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">sGrape</span><span class="hl-default"> = </span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Grape</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">sYear</span><span class="hl-default"> = </span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Year</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Error opening file</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">))
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Error opening file</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">))
</span><span class="hl-identifier">file_dialog</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">FileChooserDialog</span><span class="hl-brackets">(</span><span class="hl-identifier">title</span><span class="hl-code">=</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Select Project</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
</span><span class="hl-identifier">filter</span><span class="hl-code">.</span><span class="hl-identifier">set_name</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">pyWine database</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">))
</span><span class="hl-identifier">filter</span><span class="hl-code">.</span><span class="hl-identifier">set_name</span><span class="hl-brackets">(</span><span class="hl-identifier">_</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">All files</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">))</span></pre></div></div>
<p>The next step is to generate the Portable Object Template file using the command line <a href="http://www.gnu.org/software/gettext/manual/gettext.html">gettext</a> utility.  If you are using Linux or OS X this should be no problem and gettext will probably already be installed.  If you are using Windows the tools are available as .exe files from the <a href="ftp://ftp.gnu.org/gnu/gettext/">GNU gettext FTP</a> or this <a href="http://gettext.sourceforge.net/">gettext for Win32</a> sourceforge project.</p>
<p><span id="more-48"></span></p>
<p>To create the PO Template you need to pass the gettext utility some information.  The first thing is that the file being scanned is a python source file:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre>--language=Python</pre></div></div>
<p>The second is that we only want to translate our marked strings:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre>--keyword=_</pre></div></div>
<p>The third is that we want to create a .pot (PO template) file:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre>--output=pywine.pot</pre></div></div>
<p>And the final argument is the file that we want to get the strings from:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre>pywine.py</pre></div></div>
<p>So to put it all together it will look something like this:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre>xgettext --language=Python --keyword=_ --output=pywine.pot pywine.py</pre></div></div>
<p>That would be all that we would have to use, but since we used Glade for our GUI we also have to extract and translate the strings from our Glade project.  To do so the following line is used (taken from the great PyGTK FAQ entry: <a href="http://www.async.com.br/faq/pygtk/index.py?req=show&#038;file=faq22.002.htp">How do I internationalize a PyGTK and libglade program?</a>):</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre>intltool-extract --type=gettext/glade pywine.glade</pre></div></div>
<p>That will create the file pywine.glade.h which contains the following:</p>
<div class="hl-surround" ><div class="hl-main"><pre>char *s = N_(&quot;Add Wine&quot;);
char *s = N_(&quot;Edit Wine&quot;);
char *s = N_(&quot;Grape Variety: &quot;);
char *s = N_(&quot;PyWine&quot;);
char *s = N_(&quot;Wine&quot;);
char *s = N_(&quot;Wine:&quot;);
char *s = N_(&quot;Winery: &quot;);
char *s = N_(&quot;Year Produced: &quot;);
char *s = N_(&quot;_About&quot;);
char *s = N_(&quot;_Add&quot;);
char *s = N_(&quot;_Edit&quot;);
char *s = N_(&quot;_File&quot;);
char *s = N_(&quot;_Help&quot;);
char *s = N_(&quot;_View&quot;);</pre></div></div>
<p>You&#8217;ll notice that the strings are marked with the N_() mark.  So when we create our PO template file we will want to take into account the pywine.py file, the pywine.glad.h file, the _() mark, and the N_() mark.  To do so we will use the following command line:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre>xgettext --language=Python --keyword=_ --keyword=N_ --output=pywine.pot pywine.py pywine.glade.h</pre></div></div>
<p>This will create a file named pywine.pot that contains the following:</p>
<pre>
<code>
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <email @ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2006-12-01 23:59-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME </email><email @ADDRESS>\n"
"Language-Team: LANGUAGE <ll @li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#: pywine.py:77 pywine.glade.h:5
msgid "Wine"
msgstr ""

#: pywine.py:78
msgid "Winery"
msgstr ""

#: pywine.py:79
msgid "Grape"
msgstr ""

#: pywine.py:80
msgid "Year"
msgstr ""

#: pywine.py:176 pywine.py:178
msgid "Error opening file"
msgstr ""

#: pywine.py:229
msgid "Select Project"
msgstr ""

#: pywine.py:237
msgid "pyWine database"
msgstr ""

#: pywine.py:242
msgid "All files"
msgstr ""

#: pywine.glade.h:1
msgid "Add Wine"
msgstr ""

#: pywine.glade.h:2
msgid "Edit Wine"
msgstr ""

#: pywine.glade.h:3
msgid "Grape Variety: "
msgstr ""

#: pywine.glade.h:4
msgid "PyWine"
msgstr ""

#: pywine.glade.h:6
msgid "Wine:"
msgstr ""

#: pywine.glade.h:7
msgid "Winery: "
msgstr ""

#: pywine.glade.h:8
msgid "Year Produced: "
msgstr ""

#: pywine.glade.h:9
msgid "_About"
msgstr ""

#: pywine.glade.h:10
msgid "_Add"
msgstr ""

#: pywine.glade.h:11
msgid "_Edit"
msgstr ""

#: pywine.glade.h:12
msgid "_File"
msgstr ""

#: pywine.glade.h:13
msgid "_Help"
msgstr ""

#: pywine.glade.h:14
msgid "_View"
msgstr ""
</ll></email></code>
</pre>
<p>As you can see it contains both the translatable strings from the pywine.py file and the pywine.glade.h file.  The next thing that we need to do is create a .PO (Portable Object) file.  Each .PO file represents a specific translation to use.  To create our PO file we will use the PO template file that we just created.  We will use the msginit command line utility.</p>
<p>When running msginit we need to tell it what the input file is:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre>--input=pywine.pot</pre></div></div>
<p>And what our current locale is.  For me since I am an English speaking Canadian mine would be en_CA, which is language_COUNTRY.   If I spoke Quebecois fluently (French Canadian) I might use fr_CA.  You specify the locale as follows:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre>--locale=en_CA</pre></div></div>
<p>If you want to see what locale you are, enter <code>locale -a</code> on the command line on a Unix-like operating system.</p>
<p>The command line to enter is as follows to create an English Canadian file:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre>msginit --input=pywine.pot --locale=en_CA</pre></div></div>
<p>Let&#8217;s create an English US file as well</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre>msginit --input=pywine.pot --locale=en_US</pre></div></div>
<p>You may be asked for your email address so that someone can email you about your translation.  If you entered the above you should see: en_CA.po and a en_US.po created in your pywine directory.  Let&#8217;s edit this two files so that they are slightly different:</p>
<p>en_CA.po:</p>
<div class="hl-surround" ><div class="hl-main"><pre>#: pywine.py:77 pywine.glade.h:5
msgid &quot;Wine&quot;
msgstr &quot;Wine Canadian Style&quot;</pre></div></div>
<p>en_US.po:</p>
<div class="hl-surround" ><div class="hl-main"><pre>#: pywine.py:77 pywine.glade.h:5
msgid &quot;Wine&quot;
msgstr &quot;Wine US Style&quot;</pre></div></div>
<p> The next thing that we need to do is create the .mo file, which is the file that the gettext module will use for translation.  The .mo files are simply binary versions of the ACSII .po files.</p>
<p>We first have to create the correct directory for the .mo files.  Which is explained in bindtextdomain function for the Python gettext module:</p>
<blockquote><p>bindtextdomain(  domain[, localedir])<br />
    Bind the domain to the locale directory localedir. More concretely, gettext will look for binary .mo files for the given domain using the path (on Unix): localedir/language/LC_MESSAGES/domain.mo, where languages is searched for in the environment variables LANGUAGE, LC_ALL, LC_MESSAGES, and LANG respectively.
</p></blockquote>
<p>So for the above example we need to create the following directories:</p>
<div class="hl-surround" ><div class="hl-main"><pre>en_CA/LC_MESSAGES
en_US/LC_MESSAGES</pre></div></div>
<p>Which can be done using the following command:</p>
<div class="hl-surround" ><div class="hl-main"><pre>mkdir -p en_CA/LC_MESSAGES
mkdir -p en_US/LC_MESSAGES</pre></div></div>
<p>Now we use the msgfmt command line program to create our .mo files in the correct directory:</p>
<div class="hl-surround" ><div class="hl-main"><pre>msgfmt --output-file=en_CA/LC_MESSAGES/pywine.mo en_CA.po
msgfmt --output-file=en_US/LC_MESSAGES/pywine.mo en_US.po</pre></div></div>
<h2>The Python Code</h2>
<p>Now that we have our translation files ready we need to actually implement some python code in order to use them.  Fortunately there really isn&#8217;t that much code for us to write, but I found a real lack of examples or information on the correct way to do this, and the code to be somewhat confusing.</p>
<p>The first step is to import the <a href="http://docs.python.org/lib/module-gettext.html">gettext</a> and <a href="http://docs.python.org/lib/module-locale.html">locale</a> modules that we will use.</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">import </span><span class="hl-identifier">locale
</span><span class="hl-reserved">import </span><span class="hl-identifier">gettext</span></pre></div></div>
<p>The next step is to create a simple define for the domain of our translations, which is generally just the application name.  The gettext module will use the domain name to look for .mo files.  So if you domain is set to &#8220;12345&#8243;  gettext will look for 12345.mo files.  You&#8217;ll notice that we created our .mo files are pywine.mo above so we are going to use &#8220;pywine&#8221; as our domain:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre><span class="hl-identifier">APP_NAME</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">pywine</span><span class="hl-quotes">&quot;</span></pre></div></div>
<p>Now, in general on a Unix like operating system you would install your .mo files into the correct locations in the /usr/lshare/locale directory.  But since we are not installing this application and we want it to be easily portable to other operating systems, we will be storing the translations in the local directory.</p>
<p>Here is all of the code that we need to add to the __init__() function of the pywine class.  It is quite complicated but hopefully the comments and my explanation after will make it easier to understand:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-comment">#Translation stuff

#Get the local directory since we are not installing anything
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">local_path</span><span class="hl-default"> = </span><span class="hl-identifier">os</span><span class="hl-default">.</span><span class="hl-identifier">path</span><span class="hl-default">.</span><span class="hl-identifier">realpath</span><span class="hl-brackets">(</span><span class="hl-identifier">os</span><span class="hl-code">.</span><span class="hl-identifier">path</span><span class="hl-code">.</span><span class="hl-identifier">dirname</span><span class="hl-brackets">(</span><span class="hl-identifier">sys</span><span class="hl-code">.</span><span class="hl-identifier">argv</span><span class="hl-brackets">[</span><span class="hl-number">0</span><span class="hl-brackets">]))
</span><span class="hl-comment"># Init the list of languages to support
</span><span class="hl-identifier">langs</span><span class="hl-default"> = </span><span class="hl-brackets">[]
</span><span class="hl-comment">#Check the default locale
</span><span class="hl-identifier">lc</span><span class="hl-default">, </span><span class="hl-identifier">encoding</span><span class="hl-default"> = </span><span class="hl-identifier">locale</span><span class="hl-default">.</span><span class="hl-identifier">getdefaultlocale</span><span class="hl-brackets">()
</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">lc</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-comment">#If we have a default, it's the first in the list
	</span><span class="hl-identifier">langs</span><span class="hl-default"> = </span><span class="hl-brackets">[</span><span class="hl-identifier">lc</span><span class="hl-brackets">]
</span><span class="hl-comment"># Now lets get all of the supported languages on the system
</span><span class="hl-identifier">language</span><span class="hl-default"> = </span><span class="hl-identifier">os</span><span class="hl-default">.</span><span class="hl-identifier">environ</span><span class="hl-default">.</span><span class="hl-identifier">get</span><span class="hl-brackets">(</span><span class="hl-quotes">'</span><span class="hl-string">LANGUAGE</span><span class="hl-quotes">'</span><span class="hl-code">, </span><span class="hl-reserved">None</span><span class="hl-brackets">)
</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">language</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">langage comes back something like en_CA:en_US:en_GB:en
	on linuxy systems, on Win32 it's nothing, so we need to
	split it up into a list</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">langs</span><span class="hl-default"> += </span><span class="hl-identifier">language</span><span class="hl-default">.</span><span class="hl-identifier">split</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">:</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Now add on to the back of the list the translations that we
know that we have, our defaults</span><span class="hl-quotes">&quot;&quot;&quot;
</span><span class="hl-identifier">langs</span><span class="hl-default"> += </span><span class="hl-brackets">[</span><span class="hl-quotes">&quot;</span><span class="hl-string">en_CA</span><span class="hl-quotes">&quot;</span><span class="hl-code">, </span><span class="hl-quotes">&quot;</span><span class="hl-string">en_US</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">]

</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Now langs is a list of all of the languages that we are going
to try to use.  First we check the default, then what the system
told us, and finally the 'known' list</span><span class="hl-quotes">&quot;&quot;&quot;

</span><span class="hl-identifier">gettext</span><span class="hl-default">.</span><span class="hl-identifier">bindtextdomain</span><span class="hl-brackets">(</span><span class="hl-identifier">APP_NAME</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">local_path</span><span class="hl-brackets">)
</span><span class="hl-identifier">gettext</span><span class="hl-default">.</span><span class="hl-identifier">textdomain</span><span class="hl-brackets">(</span><span class="hl-identifier">APP_NAME</span><span class="hl-brackets">)
</span><span class="hl-comment"># Get the language to use
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">lang</span><span class="hl-default"> = </span><span class="hl-identifier">gettext</span><span class="hl-default">.</span><span class="hl-identifier">translation</span><span class="hl-brackets">(</span><span class="hl-identifier">APP_NAME</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">local_path</span><span class="hl-code">
	, </span><span class="hl-identifier">languages</span><span class="hl-code">=</span><span class="hl-identifier">langs</span><span class="hl-code">, </span><span class="hl-identifier">fallback</span><span class="hl-code"> = </span><span class="hl-reserved">True</span><span class="hl-brackets">)
</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Install the language, map _() (which we marked our
strings to translate with) to self.lang.gettext() which will
translate them.</span><span class="hl-quotes">&quot;&quot;&quot;
</span><span class="hl-identifier">_</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">lang</span><span class="hl-default">.</span><span class="hl-identifier">gettext</span></pre></div></div>
<p>Now I will explain the code in smaller pieces so that it is easier to understand what is happening.  The first step is to get the path to the python file that is running, it&#8217;s pretty straight forward so I won&#8217;t explain it her.  </p>
<p>The first <em>real</em> step is get get a list of languages, or locales that we want to support.  This list will be used to determine both the order in which we search for .mo files and what .mo files we look for.</p>
<p>The first locale we get is default locale, we use the locale module to get this:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-comment"># Init the list of languages to support
</span><span class="hl-identifier">langs</span><span class="hl-default"> = </span><span class="hl-brackets">[]
</span><span class="hl-comment">#Check the default locale
</span><span class="hl-identifier">lc</span><span class="hl-default">, </span><span class="hl-identifier">encoding</span><span class="hl-default"> = </span><span class="hl-identifier">locale</span><span class="hl-default">.</span><span class="hl-identifier">getdefaultlocale</span><span class="hl-brackets">()
</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">lc</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-comment">#If we have a default, it's the first in the list
	</span><span class="hl-identifier">langs</span><span class="hl-default"> = </span><span class="hl-brackets">[</span><span class="hl-identifier">lc</span><span class="hl-brackets">]</span></pre></div></div>
<p>The next step is to get the list of languages supported from the operating system environment.  The string that is returned by this needs to be parsed up into a list so we will also handle that. On Win32 and OS X this value is not set, so we need to make sure we check if it is None:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-comment"># Now lets get all of the supported languages on the system
</span><span class="hl-identifier">language</span><span class="hl-default"> = </span><span class="hl-identifier">os</span><span class="hl-default">.</span><span class="hl-identifier">environ</span><span class="hl-default">.</span><span class="hl-identifier">get</span><span class="hl-brackets">(</span><span class="hl-quotes">'</span><span class="hl-string">LANGUAGE</span><span class="hl-quotes">'</span><span class="hl-code">, </span><span class="hl-reserved">None</span><span class="hl-brackets">)
</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">language</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">langage comes back something like en_CA:en_US:en_GB:en
	on linuxy systems, on Win32 it's nothing, so we need to
	split it up into a list</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">langs</span><span class="hl-default"> += </span><span class="hl-identifier">language</span><span class="hl-default">.</span><span class="hl-identifier">split</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">:</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span></pre></div></div>
<p>The final step in building our locale (or language) list is to add on the locales that we know we support, i.e. the .mo files that we have.  We can add these to the list in whatever order we want, or if we are sure that they are all installed we could just add what we consider to be the default locale since we are sure that it will be found.</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Now add on to the back of the list the translations that we
know that we have, our defaults</span><span class="hl-quotes">&quot;&quot;&quot;
</span><span class="hl-identifier">langs</span><span class="hl-default"> += </span><span class="hl-brackets">[</span><span class="hl-quotes">&quot;</span><span class="hl-string">en_CA</span><span class="hl-quotes">&quot;</span><span class="hl-code">, </span><span class="hl-quotes">&quot;</span><span class="hl-string">en_US</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">]</span></pre></div></div>
<p>Now the reason that we add these last, is so that we can let the user&#8217;s language settings control what .mo file is actually used to for the translations.  We add our own just in case nothing is found.</p>
<p>The next step is to bind our domain (pywine) to a specific folder (the local folder) and then to set the current domain, this will be used when we perform the search for .mo files:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-identifier">gettext</span><span class="hl-default">.</span><span class="hl-identifier">bindtextdomain</span><span class="hl-brackets">(</span><span class="hl-identifier">APP_NAME</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">local_path</span><span class="hl-brackets">)
</span><span class="hl-identifier">gettext</span><span class="hl-default">.</span><span class="hl-identifier">textdomain</span><span class="hl-brackets">(</span><span class="hl-identifier">APP_NAME</span><span class="hl-brackets">)</span></pre></div></div>
<p>Here are the explanations for both functions from the <a href="http://docs.python.org/lib/node732.html">python documentation</a>:</p>
<blockquote><p>
bindtextdomain( domain[, localedir])<br />
    Bind the domain to the locale directory localedir. More concretely, gettext will look for binary .mo files for the given domain using the path (on Unix): localedir/language/LC_MESSAGES/domain.mo, where languages is searched for in the environment variables LANGUAGE, LC_ALL, LC_MESSAGES, and LANG respectively.<br />
    If localedir is omitted or None, then the current binding for domain is returned.</p>
<p>textdomain(  [domain])<br />
    Change or query the current global domain. If domain is None, then the current global domain is returned, otherwise the global domain is set to domain, which is returned.
</p></blockquote>
<p>The next step is to get the actual translation that we want to use and connect the getttext with the .mo file:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-comment"># Get the language to use
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">lang</span><span class="hl-default"> = </span><span class="hl-identifier">gettext</span><span class="hl-default">.</span><span class="hl-identifier">translation</span><span class="hl-brackets">(</span><span class="hl-identifier">APP_NAME</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">local_path</span><span class="hl-code">
	, </span><span class="hl-identifier">languages</span><span class="hl-code">=</span><span class="hl-identifier">langs</span><span class="hl-code">, </span><span class="hl-identifier">fallback</span><span class="hl-code"> = </span><span class="hl-reserved">True</span><span class="hl-brackets">)</span></pre></div></div>
<p>The gettext.translation() function is explained as follows:</p>
<blockquote><p>
translation( domain[, localedir[, languages[, class_[, fallback[, codeset]]]]])<br />
    Return a Translations instance based on the domain, localedir, and languages, which are first passed to find() to get a list of the associated .mo file paths. Instances with identical .mo file names are cached. The actual class instantiated is either class_ if provided, otherwise GNUTranslations. The class&#8217;s constructor must take a single file object argument. If provided, codeset will change the charset used to encode translated strings.<br />
    If multiple files are found, later files are used as fallbacks for earlier ones. To allow setting the fallback, copy.copy is used to clone each translation object from the cache; the actual instance data is still shared with the cache.<br />
    If no .mo file is found, this function raises IOError if fallback is false (which is the default), and returns a NullTranslations instance if fallback is true.
</p></blockquote>
<p>So the function will search for .mo files to each of the languages in langs in order and then returns a <a href="http://docs.python.org/lib/node735.html">GNUTranslations</a> object on success or a <a href="http://docs.python.org/lib/node734.html">NullTranslations</a> object on fail (since we set fallback to True.)</p>
<p>We then bind the object returned by translation&#8217;s gettext function to the _.  So when you call _(&#8221;xxx&#8221;) you are really calling self.lang.gettext(&#8221;xxx&#8221;).  Calling _(&#8221;XXX&#8221;) is just a convenient shorthand and somewhat of a standard way to &#8220;mark&#8221; strings for translation, as we saw above.</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Install the language, map _() (which we marked our
strings to translate with) to self.lang.gettext() which will
translate them.</span><span class="hl-quotes">&quot;&quot;&quot;
</span><span class="hl-identifier">_</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">lang</span><span class="hl-default">.</span><span class="hl-identifier">gettext</span></pre></div></div>
<p>So that&#8217;s really all you have to do.  Now if you execute py wine using the following:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre>PyWine$ LANG=en_CA python pywine.py</pre></div></div>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/pywine_03/pywine_03_01png" alt="Python translation" border="0" /></p>
<p>You will get the &#8220;Wine Canadian Style&#8221; header, then if you execute it as follows (on a Unix like OS):</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre>PyWine$ LANG=en_CA python pywine.py</pre></div></div>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/pywine_03/pywine_03_02png" alt="Python translation" border="0" /></p>
<p>You will get the &#8220;Wine US Style&#8221; header.  Then if you execute it with French from France (on a Unix like OS):</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre>PyWine$ LANG=fr_FR python pywine.py</pre></div></div>
<p>The results depends on how your system is configured, and you will either get the Canadian text or the USA text.</p>
<p>The full source and necessary files for this tutorial can be downloaded <a href="http://www.learningpython.com/sources/pywine_03.tar.gz">here</a>.</p>
<h2>Conclusion</h2>
<p>So that it for translation, using this method should work on all available operating systems with no problem.  I wanted to include a language file for totally different language (i.e. a French, or Spanish, or whatever .mo file) but my skills in multiple spoken languages are not that strong.</p>
<p>So if anyone wants to perform a simple translation on the pywine.pot file and sent it to me that would be very much appreciated.  Then we could start getting people to test this on systems with different language settings.</p>
<p>While researching this tutorial I came across some really good information from these sites:</p>
<ul>
<li><a href="http://wiki.wxpython.org/index.cgi/Internationalization">Python, wxPython and internationalization (i18n)</a></li>
<li><a href="http://www.async.com.br/faq/pygtk/index.py?req=show&#038;file=faq22.002.htp">How do I internationalize a PyGTK and libglade program?</a></li>
</ul>
<p>As always if you come across any bugs or find any problems in the code please let me know!</p>
<div style="float:right;margin:0px 0px 0px 0px;"><a href="http://www.google.com/reader/link?url=http://www.learningpython.com/2006/12/03/translating-your-pythonpygtk-application/&title=Translating your Python/PyGTK application&srcTitle=learning python&srcURL=http://www.learningpython.com"target="_blank" rel=""><img border="0" src="http://www.learningpython.com/wp-content/plugins/wp-google-buzz/icon/12.png" style="opacity:1;filter:alpha(opacity=100)" onmouseover="this.style.opacity=0.8;this.filters.alpha.opacity=70" onmouseout="this.style.opacity=1;this.filters.alpha.opacity=100"/> </a></div>]]></content:encoded>
			<wfw:commentRss>http://www.learningpython.com/2006/12/03/translating-your-pythonpygtk-application/feed/</wfw:commentRss>
		<slash:comments>35</slash:comments>
		</item>
		<item>
		<title>Novemeber</title>
		<link>http://www.learningpython.com/2006/11/04/novemeber/</link>
		<comments>http://www.learningpython.com/2006/11/04/novemeber/#comments</comments>
		<pubDate>Sat, 04 Nov 2006 17:40:14 +0000</pubDate>
		<dc:creator>selsine</dc:creator>
				<category><![CDATA[News]]></category>
		<category><![CDATA[PyGTK]]></category>
		<category><![CDATA[glade]]></category>
		<category><![CDATA[pygame]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.learningpython.com/2006/11/04/novemeber/</guid>
		<description><![CDATA[
			
				
			
		
Hi everyone just a quick post to let you know that I probably won&#8217;t be posting much during this month as I have decided to participate in nanowrimo which leaves me almost no time to come up with tutorials.
I as mentioned in the past Satoshi Tanabe over at satolog has translated a few of my [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.learningpython.com%2F2006%2F11%2F04%2Fnovemeber%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.learningpython.com%2F2006%2F11%2F04%2Fnovemeber%2F&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<p>Hi everyone just a quick post to let you know that I probably won&#8217;t be posting much during this month as I have decided to participate in <a href="http://www.nanowrimo.org/">nanowrimo</a> which leaves me almost no time to come up with tutorials.</p>
<p>I as mentioned in the past Satoshi Tanabe over at <a href="http://po3a.blogspot.com/2006/09/pygtk.html">satolog</a> has translated a few of my tutorials over into Japanese, which is very cool.  It also turns out that someone over at <a href="http://blog.chinaunix.net">blog.chinaunix.net</a> has also translated one of my tutorials (<a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">Building an application with PyGTK and Glade</a>) into <a href="http://blog.chinaunix.net/u/5462/showart.php?id=157696">Chinese</a>, again very cool!  Thanks a lot for the translations guys. </p>
<p>I&#8217;ve also been working on some pyGame in my spare time over the last few months, I&#8217;ve been working on a level editor and a widget set:</p>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/pygame/pygame_scrollbars.png" alt="PyGame Window" border="0" /></p>
<p>Of course that doesn&#8217;t show very much but it show show my scrollbars!  The problem with building the level editor is that y you always have to keep working on the smallest details (like a scrollable area0 before you can get to the real heart of the problem.  Oh well it&#8217;s till fun!</p>
<p>Talk to you all soon!</p>
<div style="float:right;margin:0px 0px 0px 0px;"><a href="http://www.google.com/reader/link?url=http://www.learningpython.com/2006/11/04/novemeber/&title=Novemeber&srcTitle=learning python&srcURL=http://www.learningpython.com"target="_blank" rel=""><img border="0" src="http://www.learningpython.com/wp-content/plugins/wp-google-buzz/icon/12.png" style="opacity:1;filter:alpha(opacity=100)" onmouseover="this.style.opacity=0.8;this.filters.alpha.opacity=70" onmouseout="this.style.opacity=1;this.filters.alpha.opacity=100"/> </a></div>]]></content:encoded>
			<wfw:commentRss>http://www.learningpython.com/2006/11/04/novemeber/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WordPy 0.2 &#8211; Using XML to Save and Load Data</title>
		<link>http://www.learningpython.com/2006/10/24/wordpy-02-using-xml-to-save-and-load-data/</link>
		<comments>http://www.learningpython.com/2006/10/24/wordpy-02-using-xml-to-save-and-load-data/#comments</comments>
		<pubDate>Wed, 25 Oct 2006 02:22:52 +0000</pubDate>
		<dc:creator>selsine</dc:creator>
				<category><![CDATA[PyGTK]]></category>
		<category><![CDATA[WordPy]]></category>
		<category><![CDATA[glade]]></category>
		<category><![CDATA[gui]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[reference]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[xml]]></category>

		<guid isPermaLink="false">http://www.learningpython.com/?p=41</guid>
		<description><![CDATA[
			
				
			
		
All right, so we have our base WordPy application running, so let&#8217;s try to extend it a bit more by letting you load and save blog posts to and from an xml file.  Please note that this tutorial simply shows one method of saving and loading data using xml, there are many different methods [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.learningpython.com%2F2006%2F10%2F24%2Fwordpy-02-using-xml-to-save-and-load-data%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.learningpython.com%2F2006%2F10%2F24%2Fwordpy-02-using-xml-to-save-and-load-data%2F&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<p>All right, so we have our base WordPy application running, so let&#8217;s try to extend it a bit more by letting you load and save blog posts to and from an xml file.  Please note that this tutorial simply shows one method of saving and loading data using xml, there are many different methods and this method was chosen for its simplicity.</p>
<p>If you are unfamiliar with the <a href="http://www.learningpython.com/2006/08/19/wordpress-python-library/">first WordPy tutorial</a> you should probably read it fist in order to have a better understanding of some of what happens in this tutorial. </p>
<p>You can download the complete code for this tutorial <a href="http://www.learningpython.com/sources/WordPy_0_2.tar.gz">here</a>.</p>
<h2>The GUI</h2>
<p>The first thing we need to-do is open up the wordpy glade project and make some changes:</p>
<ol>
<li>We&#8217;ll start off by adding another item to our VBox in Glade.  You can do this by holding down shift and clicking on the WordPy window until you see the GTKVBox come up in the properties window.  Then simply change it&#8217;s size value from 4 to 5.</li>
<li>In the empty space add a menu bar.  Then on the Packing tab of the menu bar&#8217;s properties set the position to be zero, so that the menu is at the top of the window.</li>
<li>Then edit the menu so that only the  File, Edit, and Help menu&#8217;s remain.</li>
<li>Add handlers to each of the files menu items: on_file_new, on_file_open, on_file_save, on_file_save_as</li>
</ol>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/WordPy_02/wordpy_02_01.png" alt="GLADE Window PyWine" border="0" /></p>
<h2>The Code</h2>
<p>That&#8217;s it for editing the GUI, now we have to go and edit the code.  The first step is to connect all of the menu events with our code:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-identifier">dic</span><span class="hl-default"> = {</span><span class="hl-quotes">&quot;</span><span class="hl-string">on_wndMain_destroy</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">quit</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnBold_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnBold_clicked</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnItalic_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnItalic_clicked</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnLink_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnLink_clicked</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnBlockQuote_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnBlockQuote_clicked</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnDel_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnDel_clicked</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnIns_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnIns_clicked</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnImage_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnImage_clicked</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnUnorderedList_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnUnorderedList_clicked</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnOrderedList_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnOrderedList_clicked</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnListItem_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnListItem_clicked</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnCode_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnCode_clicked</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnMore_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnMore_clicked</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnSettings_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnSettings_clicked</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnpost_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnpost_clicked</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_file_new</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_file_new</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_file_open</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_file_open</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_file_save</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_file_save</span><span class="hl-default">
		, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_file_save_as</span><span class="hl-quotes">&quot;</span><span class="hl-default">: </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_file_save_as</span><span class="hl-default">}
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">signal_autoconnect</span><span class="hl-brackets">(</span><span class="hl-identifier">dic</span><span class="hl-brackets">)</span></pre></div></div>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_file_new</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:

</span><span class="hl-reserved">def </span><span class="hl-identifier">on_file_open</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:

</span><span class="hl-reserved">def </span><span class="hl-identifier">on_file_save</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:

</span><span class="hl-reserved">def </span><span class="hl-identifier">on_file_save_as</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">. </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:</span></pre></div></div>
<p><span id="more-41"></span></p>
<p>Now when we save and load the post we will need to use the same xml tags, so instead of typing them twice (or in case we want to change them) I decided to save them as a dictionary in the WordPy class:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-identifier">MAIN_TAG</span><span class="hl-default"> = </span><span class="hl-number">0
</span><span class="hl-identifier">POST_TAG</span><span class="hl-default"> = </span><span class="hl-number">1
</span><span class="hl-identifier">TITLE_TAG</span><span class="hl-default"> = </span><span class="hl-number">2
</span><span class="hl-identifier">TEXT_TAG</span><span class="hl-default"> = </span><span class="hl-number">3
</span><span class="hl-identifier">SETTINGS_TAG</span><span class="hl-default"> = </span><span class="hl-number">4
</span><span class="hl-identifier">URL_TAG</span><span class="hl-default"> = </span><span class="hl-number">5
</span><span class="hl-identifier">USERNAME_TAG</span><span class="hl-default"> = </span><span class="hl-number">6
</span><span class="hl-identifier">PASSWORD_TAG</span><span class="hl-default"> = </span><span class="hl-number">7

</span><span class="hl-reserved">class </span><span class="hl-identifier">WordPy</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This is the Wordpy application.  It is a simple PyGTK
	application that interacts with the WorPress Python library.</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-identifier">_xml_tags</span><span class="hl-default"> = {
			</span><span class="hl-identifier">MAIN_TAG</span><span class="hl-default"> : </span><span class="hl-quotes">&quot;</span><span class="hl-string">WordPy</span><span class="hl-quotes">&quot;</span><span class="hl-default">
			, </span><span class="hl-identifier">POST_TAG</span><span class="hl-default"> : </span><span class="hl-quotes">&quot;</span><span class="hl-string">Post</span><span class="hl-quotes">&quot;</span><span class="hl-default">
			, </span><span class="hl-identifier">TITLE_TAG</span><span class="hl-default"> : </span><span class="hl-quotes">&quot;</span><span class="hl-string">Title</span><span class="hl-quotes">&quot;</span><span class="hl-default">
			, </span><span class="hl-identifier">TEXT_TAG</span><span class="hl-default"> : </span><span class="hl-quotes">&quot;</span><span class="hl-string">Text</span><span class="hl-quotes">&quot;</span><span class="hl-default">
			, </span><span class="hl-identifier">URL_TAG</span><span class="hl-default"> : </span><span class="hl-quotes">&quot;</span><span class="hl-string">URL</span><span class="hl-quotes">&quot;</span><span class="hl-default">
			, </span><span class="hl-identifier">SETTINGS_TAG</span><span class="hl-default"> : </span><span class="hl-quotes">&quot;</span><span class="hl-string">Settings</span><span class="hl-quotes">&quot;</span><span class="hl-default">
			, </span><span class="hl-identifier">USERNAME_TAG</span><span class="hl-default"> : </span><span class="hl-quotes">&quot;</span><span class="hl-string">Username</span><span class="hl-quotes">&quot;</span><span class="hl-default">
			, </span><span class="hl-identifier">PASSWORD_TAG</span><span class="hl-default"> : </span><span class="hl-quotes">&quot;</span><span class="hl-string">password</span><span class="hl-quotes">&quot;</span><span class="hl-default">
	}</span></pre></div></div>
<p>Now when we save and load we can get the username xml tag like so:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">_xml_tags</span><span class="hl-brackets">[</span><span class="hl-identifier">USERNAME_TAG</span><span class="hl-brackets">]</span></pre></div></div>
<p>And if someone wants to import WordPy into their own Python project, they could get at the tags (if they didn&#8217;t have a WordPy object instantiated) like so:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre><span class="hl-identifier">WordPy</span><span class="hl-default">.</span><span class="hl-identifier">_xml_tags</span><span class="hl-brackets">[</span><span class="hl-identifier">USERNAME_TAG</span><span class="hl-brackets">]</span></pre></div></div>
<p>We also need to add a new element in the __init__() function:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">xml_file</span><span class="hl-default"> = </span><span class="hl-reserved">None</span></pre></div></div>
<p>This will be the full path to the xml file that is represents the current post, it starts off as None, since when we first start WordPy the blank post is not associated with any xml file.</p>
<p>We will also need to do some file browsing for the save, open, and save as events so we&#8217;ll borrow the file_browse() function from the <a href="http://www.learningpython.com/2006/09/02/extending-our-pygtk-application/">Extending our PyGTK application</a> tutorial (to learn more about the function and how it works please read the tutorial):</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-comment"># Borrowed from the PyWine project
</span><span class="hl-reserved">def </span><span class="hl-identifier">file_browse</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">dialog_action</span><span class="hl-code">, </span><span class="hl-identifier">file_name</span><span class="hl-code">=</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is used to browse for a pyWine file.
	It can be either a save or open dialog depending on
	what dialog_action is.
	The path to the file will be returned if the user
	selects one, however a blank string will be returned
	if they cancel or do not select one.
	dialog_action - The open or save mode for the dialog either
	gtk.FILE_CHOOSER_ACTION_OPEN, gtk.FILE_CHOOSER_ACTION_SAVE
	@param file_name - Default name when doing a save
	@returns - File Name, or None on cancel.
	</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">dialog_action</span><span class="hl-code">==</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_OPEN</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">dialog_buttons</span><span class="hl-default"> = </span><span class="hl-brackets">(</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">STOCK_CANCEL</span><span class="hl-code">
			, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_CANCEL</span><span class="hl-code">
			, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">STOCK_OPEN</span><span class="hl-code">
			, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_OK</span><span class="hl-brackets">)
		</span><span class="hl-identifier">dlg_title</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">Open Post</span><span class="hl-quotes">&quot;
	</span><span class="hl-reserved">else</span><span class="hl-default">:
		</span><span class="hl-identifier">dialog_buttons</span><span class="hl-default"> = </span><span class="hl-brackets">(</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">STOCK_CANCEL</span><span class="hl-code">
			, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_CANCEL</span><span class="hl-code">
			, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">STOCK_SAVE</span><span class="hl-code">
			, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_OK</span><span class="hl-brackets">)
		</span><span class="hl-identifier">dlg_title</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">Save Post</span><span class="hl-quotes">&quot;

	</span><span class="hl-identifier">file_dialog</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">FileChooserDialog</span><span class="hl-brackets">(</span><span class="hl-identifier">title</span><span class="hl-code">=</span><span class="hl-identifier">dlg_title</span><span class="hl-code">
		, </span><span class="hl-identifier">action</span><span class="hl-code">=</span><span class="hl-identifier">dialog_action</span><span class="hl-code">
		, </span><span class="hl-identifier">buttons</span><span class="hl-code">=</span><span class="hl-identifier">dialog_buttons</span><span class="hl-brackets">)
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">set the filename if we are saving</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">dialog_action</span><span class="hl-code">==</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_SAVE</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">set_current_name</span><span class="hl-brackets">(</span><span class="hl-identifier">file_name</span><span class="hl-brackets">)
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Create and add the pywine filter</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">filter</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">FileFilter</span><span class="hl-brackets">()
	</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">set_name</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">WordPy Post</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">add_pattern</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">*.</span><span class="hl-quotes">&quot;</span><span class="hl-code"> + </span><span class="hl-identifier">FILE_EXT</span><span class="hl-brackets">)
	</span><span class="hl-identifier">file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">add_filter</span><span class="hl-brackets">(</span><span class="hl-identifier">filter</span><span class="hl-brackets">)
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">dialog_action</span><span class="hl-code">==</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_OPEN</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Create and add the 'all files' filter</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">filter</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">FileFilter</span><span class="hl-brackets">()
		</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">set_name</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">All files</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">add_pattern</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">*</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-identifier">file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">add_filter</span><span class="hl-brackets">(</span><span class="hl-identifier">filter</span><span class="hl-brackets">)

	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Init the return value</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">result</span><span class="hl-default"> = </span><span class="hl-reserved">None
	if </span><span class="hl-identifier">file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">run</span><span class="hl-brackets">()</span><span class="hl-default"> == </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">RESPONSE_OK</span><span class="hl-default">:
		</span><span class="hl-identifier">result</span><span class="hl-default"> = </span><span class="hl-identifier">file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">get_filename</span><span class="hl-brackets">()
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">dialog_action</span><span class="hl-code">==</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_SAVE</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-identifier">result</span><span class="hl-default">, </span><span class="hl-identifier">extension</span><span class="hl-default"> = </span><span class="hl-identifier">os</span><span class="hl-default">.</span><span class="hl-identifier">path</span><span class="hl-default">.</span><span class="hl-identifier">splitext</span><span class="hl-brackets">(</span><span class="hl-identifier">result</span><span class="hl-brackets">)
			</span><span class="hl-identifier">result</span><span class="hl-default"> = </span><span class="hl-identifier">result</span><span class="hl-default"> + </span><span class="hl-quotes">&quot;</span><span class="hl-string">.</span><span class="hl-quotes">&quot;</span><span class="hl-default"> + </span><span class="hl-identifier">FILE_EXT
	file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">destroy</span><span class="hl-brackets">()

	</span><span class="hl-reserved">return </span><span class="hl-identifier">result</span></pre></div></div>
<p>Note that this function is somewhat different then the original in that it returns None if the users cancels the operation rather then returning a blank string.</p>
<p>I set FILE_EXT to &#8220;wpx&#8221;, which stands for WordPy xml, at the beginning of the WordPy.py file near the top:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre><span class="hl-identifier">FILE_EXT</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">wpx</span><span class="hl-quotes">&quot;</span></pre></div></div>
<p>Another helper function that I added is the set_post_text function:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">set_post_text</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">text</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Simple Helper function that sets the text
	in the text View.
	@param text - The text that will be put into
	the text view.
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-comment">#select all of the text
	</span><span class="hl-identifier">start</span><span class="hl-default">, </span><span class="hl-identifier">end</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">txtBuffer</span><span class="hl-default">.</span><span class="hl-identifier">get_bounds</span><span class="hl-brackets">()
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">txtBuffer</span><span class="hl-default">.</span><span class="hl-identifier">select_range</span><span class="hl-brackets">(</span><span class="hl-identifier">end</span><span class="hl-code">,</span><span class="hl-identifier">start</span><span class="hl-brackets">)
	</span><span class="hl-comment">#insert over the selection i.e. replace all the text
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">insert_text</span><span class="hl-brackets">(</span><span class="hl-identifier">text</span><span class="hl-brackets">)
	</span><span class="hl-comment">#put the selection at the end.
	</span><span class="hl-identifier">start</span><span class="hl-default">, </span><span class="hl-identifier">end</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">txtBuffer</span><span class="hl-default">.</span><span class="hl-identifier">get_bounds</span><span class="hl-brackets">()
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">txtBuffer</span><span class="hl-default">.</span><span class="hl-identifier">select_range</span><span class="hl-brackets">(</span><span class="hl-identifier">end</span><span class="hl-code">,</span><span class="hl-identifier">end</span><span class="hl-brackets">)</span></pre></div></div>
<p>It&#8217;s a simple helper function that sets the post text.  It does this by selecting all of the text in the gtk.TextView and then calls the insert_text() function (detailed in the  previous tutorial) which will overwrite what is selected in the gtk.TextView (in this case everything) with the text that it is passed.  After that we simply move the cursor caret to the end of the text.</p>
<h3>Saving the post</h3>
<p>Now that we have all of the base code in place we can start work on actually saving our file to xml, to do that we will use the <a href="http://docs.python.org/lib/module-xml.dom.minidom.html">xml.dom.minidom</a> module.  The reason that I decided to use the xml.dom.minidom object as opposed to the xml.dom object is because the:</p>
<blockquote><p>xml.dom.minidom is a light-weight implementation of the Document Object Model interface. It is intended to be simpler than the full DOM and also significantly smaller.</p></blockquote>
<p>This is perfect for us since we for saving and loading we do not need to perform any exceedingly difficult xml tasks.</p>
<p>The first function we are going to implement is the on_file_save() function:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_file_save</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function saves the current post as an XML file</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-comment"># Let the user browse for the save location and name
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">xml_file</span><span class="hl-code"> == </span><span class="hl-reserved">None</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">xml_file</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">file_browse</span><span class="hl-brackets">(</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_SAVE</span><span class="hl-brackets">)
	</span><span class="hl-comment">#If we have a xml_file
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">xml_file</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">xml_save_to_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">xml_file</span><span class="hl-brackets">))</span><span class="hl-default">:
			</span><span class="hl-comment">#Allright it all worked! Set the Title
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">set_window_title_from_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">xml_file</span><span class="hl-brackets">)</span></pre></div></div>
<p>This function is pretty simple since it relies on many other functions, this was done so that a lot of the code could be re-used by the on_file_save_as() function. The first thing we do is check to see if self.xml_name has been set, if it hasn&#8217;t we pop up a save dialog and let the user select the location where their file will be saved.  Then if they chose a filename rather then cancelling we will actually start saving our data to xml.</p>
<p>The two main functions we use to accomplish this are xml_save_to_file() and set_window_title_file().  set_window_title_file() is a very simple function that sets the main windows title based upon a file path:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">set_window_title_from_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">xml_file</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Set the windows title, take it from xml_file.
	@param xml_file - string - The xml file name that we will
	base the window title off of
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">xml_file</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">main_window</span><span class="hl-default">.</span><span class="hl-identifier">set_title</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">WordPy - %s</span><span class="hl-quotes">&quot;</span><span class="hl-code">
			% </span><span class="hl-brackets">(</span><span class="hl-identifier">os</span><span class="hl-code">.</span><span class="hl-identifier">path</span><span class="hl-code">.</span><span class="hl-identifier">basename</span><span class="hl-brackets">(</span><span class="hl-identifier">xml_file</span><span class="hl-brackets">)))
	</span><span class="hl-reserved">else</span><span class="hl-default">:
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">main_window</span><span class="hl-default">.</span><span class="hl-identifier">set_title</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">WordPy - Untitled</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span></pre></div></div>
<p>The more complicated function is the xml_save_to_file() function:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">xml_save_to_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">xml_file</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Save the current post to xml_file
	@param xml_file - string - path to file that
	we will save the xml to.
	@returns boolean - True success. False failure
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-comment">#Init return value
	</span><span class="hl-identifier">success</span><span class="hl-default"> = </span><span class="hl-reserved">False

	</span><span class="hl-comment">#Get the available DOM Implementation
	</span><span class="hl-identifier">impl</span><span class="hl-default"> = </span><span class="hl-identifier">minidom</span><span class="hl-default">.</span><span class="hl-identifier">getDOMImplementation</span><span class="hl-brackets">()
	</span><span class="hl-comment">#Create the document, with wordpy as to base node
	</span><span class="hl-identifier">xml_document</span><span class="hl-default"> = </span><span class="hl-identifier">impl</span><span class="hl-default">.</span><span class="hl-identifier">createDocument</span><span class="hl-brackets">(</span><span class="hl-reserved">None</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">_xml_tags</span><span class="hl-brackets">[</span><span class="hl-identifier">MAIN_TAG</span><span class="hl-brackets">]</span><span class="hl-code">, </span><span class="hl-reserved">None</span><span class="hl-brackets">)
	</span><span class="hl-comment">#Save the post settings into the xml
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">xml_save</span><span class="hl-brackets">(</span><span class="hl-identifier">xml_document</span><span class="hl-brackets">)
	</span><span class="hl-comment">#Save the Blog settings into the XML
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">BlogSettings</span><span class="hl-default">.</span><span class="hl-identifier">xml_save</span><span class="hl-brackets">(</span><span class="hl-identifier">xml_document</span><span class="hl-brackets">)
	</span><span class="hl-comment">#Now actually try to save the file
	</span><span class="hl-reserved">try</span><span class="hl-default">:
		</span><span class="hl-identifier">save_file</span><span class="hl-default"> = </span><span class="hl-builtin">open</span><span class="hl-brackets">(</span><span class="hl-identifier">xml_file</span><span class="hl-code">, </span><span class="hl-quotes">'</span><span class="hl-string">w</span><span class="hl-quotes">'</span><span class="hl-brackets">)
		</span><span class="hl-comment">#write the xml document to disc
		</span><span class="hl-identifier">xml_document</span><span class="hl-default">.</span><span class="hl-identifier">documentElement</span><span class="hl-default">.</span><span class="hl-identifier">writexml</span><span class="hl-brackets">(</span><span class="hl-identifier">save_file</span><span class="hl-brackets">)
		</span><span class="hl-identifier">save_file</span><span class="hl-default">.</span><span class="hl-identifier">close</span><span class="hl-brackets">()
	</span><span class="hl-reserved">except IOError</span><span class="hl-default">, </span><span class="hl-brackets">(</span><span class="hl-identifier">errno</span><span class="hl-code">, </span><span class="hl-identifier">strerror</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(
			</span><span class="hl-quotes">&quot;</span><span class="hl-string">Error saving post(%s): %s</span><span class="hl-quotes">&quot;</span><span class="hl-code"> % </span><span class="hl-brackets">(</span><span class="hl-identifier">errno</span><span class="hl-code">, </span><span class="hl-identifier">strerror</span><span class="hl-brackets">))
	</span><span class="hl-reserved">else</span><span class="hl-default">:
		</span><span class="hl-comment">#Allright it all worked! Set the return value
		</span><span class="hl-identifier">success</span><span class="hl-default"> = </span><span class="hl-reserved">True

	return </span><span class="hl-identifier">success</span></pre></div></div>
<p>The code is a bit complicated so I will take a little bit of time trying to explain each part in it.  The first thing that we do (after initializing the return value) is call minidom.GetDomImplementation() to get a suitable DOM implementation.  You can read more about the Document Object Model in the python documentation <a href="http://docs.python.org/lib/module-xml.dom.html">here</a>.  We then use that DOM implementation to create our xml document using self._xml_tags[MAIN_TAG] as the main tag for our xml document.  Basically this creates:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre>&lt;WordPy&gt;&lt;/WordPy&gt;</pre></div></div>
<p>Then we call two functions self.xml_save() and self.BlogSettings.xml_save() which are functions that will actually save our data into the xml document.  Then we try to open our self.xml_file and write the xml document into that file using the writexml function:</p>
<blockquote><p><strong>writexml(  	writer[,indent=""[,addindent=""[,newl=""]]])</strong><br />
Write XML to the writer object. The writer should have a write() method which matches that of the file object interface. The indent parameter is the indentation of the current node. The addindent parameter is the incremental indentation to use for subnodes of the current one. The newl parameter specifies the string to use to terminate newlines.</p></blockquote>
<p>After that we simply set the windows title based on self.xml_file so that the user knows which file they are currently working with.</p>
<p>The next function to look at is the WordPy.xml_save() function which actually looks more complicated then it really is:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">xml_save</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">xml_document</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Save the current blog post to an xml document.
	@param xml_document - xml.dom.minidom.Document object -
	The xml document that we will save the post to.</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-comment">#First create the &quot;post&quot; xml element
	</span><span class="hl-identifier">post_element</span><span class="hl-default"> = </span><span class="hl-identifier">xml_document</span><span class="hl-default">.</span><span class="hl-identifier">createElement</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">_xml_tags</span><span class="hl-brackets">[</span><span class="hl-identifier">POST_TAG</span><span class="hl-brackets">])  </span><span class="hl-comment"># creates &lt;post&gt;&lt;/post&gt;
	#Title
	</span><span class="hl-identifier">title</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">enTitle</span><span class="hl-default">.</span><span class="hl-identifier">get_text</span><span class="hl-brackets">()
	</span><span class="hl-comment">#Text
	</span><span class="hl-identifier">start</span><span class="hl-default">, </span><span class="hl-identifier">end</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">txtBuffer</span><span class="hl-default">.</span><span class="hl-identifier">get_bounds</span><span class="hl-brackets">()
	</span><span class="hl-identifier">text</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">txtBuffer</span><span class="hl-default">.</span><span class="hl-identifier">get_text</span><span class="hl-brackets">(</span><span class="hl-identifier">start</span><span class="hl-code">, </span><span class="hl-identifier">end</span><span class="hl-brackets">)
	</span><span class="hl-comment"># creates &lt;title&gt;&lt;/title&gt;
	</span><span class="hl-identifier">title_element</span><span class="hl-default"> = </span><span class="hl-identifier">xml_document</span><span class="hl-default">.</span><span class="hl-identifier">createElement</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">_xml_tags</span><span class="hl-brackets">[</span><span class="hl-identifier">TITLE_TAG</span><span class="hl-brackets">])
	</span><span class="hl-comment">#Create &lt;title&gt;title text&lt;/title&gt;
	</span><span class="hl-identifier">title_element</span><span class="hl-default">.</span><span class="hl-identifier">appendChild</span><span class="hl-brackets">(</span><span class="hl-identifier">xml_document</span><span class="hl-code">.</span><span class="hl-identifier">createTextNode</span><span class="hl-brackets">(</span><span class="hl-identifier">title</span><span class="hl-brackets">))
	</span><span class="hl-comment"># creates &lt;text&gt;&lt;/text&gt;
	</span><span class="hl-identifier">text_element</span><span class="hl-default"> = </span><span class="hl-identifier">xml_document</span><span class="hl-default">.</span><span class="hl-identifier">createElement</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">_xml_tags</span><span class="hl-brackets">[</span><span class="hl-identifier">TEXT_TAG</span><span class="hl-brackets">])
	</span><span class="hl-comment"># creates &lt;text&gt;text&lt;/text&gt;
	</span><span class="hl-identifier">text_element</span><span class="hl-default">.</span><span class="hl-identifier">appendChild</span><span class="hl-brackets">(</span><span class="hl-identifier">xml_document</span><span class="hl-code">.</span><span class="hl-identifier">createTextNode</span><span class="hl-brackets">(</span><span class="hl-identifier">text</span><span class="hl-brackets">))
	</span><span class="hl-comment">#Now create &lt;post&gt;&lt;title&gt;title text&lt;/title&gt;&lt;text&gt;text&lt;/text&gt;&lt;/post&gt;
	</span><span class="hl-identifier">post_element</span><span class="hl-default">.</span><span class="hl-identifier">appendChild</span><span class="hl-brackets">(</span><span class="hl-identifier">title_element</span><span class="hl-brackets">)
	</span><span class="hl-identifier">post_element</span><span class="hl-default">.</span><span class="hl-identifier">appendChild</span><span class="hl-brackets">(</span><span class="hl-identifier">text_element</span><span class="hl-brackets">)
	</span><span class="hl-comment">#Now add to the xml docuemnt
	</span><span class="hl-identifier">xml_document</span><span class="hl-default">.</span><span class="hl-identifier">documentElement</span><span class="hl-default">.</span><span class="hl-identifier">appendChild</span><span class="hl-brackets">(</span><span class="hl-identifier">post_element</span><span class="hl-brackets">)</span></pre></div></div>
<p>Basically it&#8217;s quite simple, we call  xml_document.createElement() to create an xml element (see <a href="http://docs.python.org/lib/dom-document-objects.html">this page</a> for more information on functions available to our Document object.)  Then if we want we create a text node (and xml node that contains a text string) using xml_document.createTextNode() and add that to our newly created xml element.</p>
<p>So the first thing we do is create the post element, and then we create the title and text elements and add those to the post element.  Then we add the post element to the root of our xml document using documentElement which always represents the root element of our xml document.  It&#8217;s really quite simple, hopefully the comments in the code will help to explain each basic step.</p>
<p>The other xml_save function we have is the WordPressBlogSettings.xml_save() function which is very similar to the other xml_save() function:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">xml_save</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">xml_document</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Save the current blog post to an xml document.
	@param xml_document - xml.dom.minidom.Document object -
	The xml document that we will save the post to.</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-comment">#First create the &quot;settings&quot; xml element
	</span><span class="hl-identifier">settings_element</span><span class="hl-default"> = </span><span class="hl-identifier">xml_document</span><span class="hl-default">.</span><span class="hl-identifier">createElement</span><span class="hl-brackets">(</span><span class="hl-identifier">WordPy</span><span class="hl-code">.</span><span class="hl-identifier">_xml_tags</span><span class="hl-brackets">[</span><span class="hl-identifier">SETTINGS_TAG</span><span class="hl-brackets">])
	</span><span class="hl-comment">#Creates &lt;url&gt;&lt;/url&gt;
	</span><span class="hl-identifier">URL_element</span><span class="hl-default"> = </span><span class="hl-identifier">xml_document</span><span class="hl-default">.</span><span class="hl-identifier">createElement</span><span class="hl-brackets">(</span><span class="hl-identifier">WordPy</span><span class="hl-code">.</span><span class="hl-identifier">_xml_tags</span><span class="hl-brackets">[</span><span class="hl-identifier">URL_TAG</span><span class="hl-brackets">])
	</span><span class="hl-comment">#Create &lt;url&gt;URL&lt;/url&gt;
	</span><span class="hl-identifier">URL_element</span><span class="hl-default">.</span><span class="hl-identifier">appendChild</span><span class="hl-brackets">(</span><span class="hl-identifier">xml_document</span><span class="hl-code">.</span><span class="hl-identifier">createTextNode</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">URL</span><span class="hl-brackets">))
	</span><span class="hl-comment">#Creates &lt;username&gt;&lt;/username&gt;
	</span><span class="hl-identifier">username_element</span><span class="hl-default"> = </span><span class="hl-identifier">xml_document</span><span class="hl-default">.</span><span class="hl-identifier">createElement</span><span class="hl-brackets">(</span><span class="hl-identifier">WordPy</span><span class="hl-code">.</span><span class="hl-identifier">_xml_tags</span><span class="hl-brackets">[</span><span class="hl-identifier">USERNAME_TAG</span><span class="hl-brackets">])
	</span><span class="hl-comment">#Creates &lt;username&gt;Username&lt;/username&gt;
	</span><span class="hl-identifier">username_element</span><span class="hl-default">.</span><span class="hl-identifier">appendChild</span><span class="hl-brackets">(</span><span class="hl-identifier">xml_document</span><span class="hl-code">.</span><span class="hl-identifier">createTextNode</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">Username</span><span class="hl-brackets">))
	</span><span class="hl-comment">#Creates &lt;password&gt;&lt;/password&gt;
	</span><span class="hl-identifier">password_element</span><span class="hl-default"> = </span><span class="hl-identifier">xml_document</span><span class="hl-default">.</span><span class="hl-identifier">createElement</span><span class="hl-brackets">(</span><span class="hl-identifier">WordPy</span><span class="hl-code">.</span><span class="hl-identifier">_xml_tags</span><span class="hl-brackets">[</span><span class="hl-identifier">PASSWORD_TAG</span><span class="hl-brackets">])
	</span><span class="hl-comment">#Creates &lt;password&gt;Password&lt;/password&gt;
	</span><span class="hl-identifier">password_element</span><span class="hl-default">.</span><span class="hl-identifier">appendChild</span><span class="hl-brackets">(</span><span class="hl-identifier">xml_document</span><span class="hl-code">.</span><span class="hl-identifier">createTextNode</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">Password</span><span class="hl-brackets">))
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Now create:
	&lt;post&gt;
		&lt;url&gt;URL&lt;/url&gt;
		&lt;username&gt;Username&lt;/username&gt;
		&lt;password&gt;Password&lt;/password&gt;
	&lt;/post&gt;
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">settings_element</span><span class="hl-default">.</span><span class="hl-identifier">appendChild</span><span class="hl-brackets">(</span><span class="hl-identifier">URL_element</span><span class="hl-brackets">)
	</span><span class="hl-identifier">settings_element</span><span class="hl-default">.</span><span class="hl-identifier">appendChild</span><span class="hl-brackets">(</span><span class="hl-identifier">username_element</span><span class="hl-brackets">)
	</span><span class="hl-identifier">settings_element</span><span class="hl-default">.</span><span class="hl-identifier">appendChild</span><span class="hl-brackets">(</span><span class="hl-identifier">password_element</span><span class="hl-brackets">)
	</span><span class="hl-comment">#Now add to the xml docuemnt
	</span><span class="hl-identifier">xml_document</span><span class="hl-default">.</span><span class="hl-identifier">documentElement</span><span class="hl-default">.</span><span class="hl-identifier">appendChild</span><span class="hl-brackets">(</span><span class="hl-identifier">settings_element</span><span class="hl-brackets">)</span></pre></div></div>
<p>After all this is set and done we end up with an xml file that looks similar to this although not spaced as nicely:</p>
<pre>
<div class="hl-surround" ><div class="hl-main"><pre>&lt;wordpy&gt;
	&lt;post&gt;
		&lt;title&gt;My Blog Post&lt;/title&gt;
		&lt;text&gt;Here is my text&lt;/text&gt;
	&lt;/post&gt;
	&lt;settings&gt;
		&lt;url&gt;	http://www.myblog.com/wordpress/xmlrpc.php&lt;/url&gt;
		&lt;username&gt;user&lt;/username&gt;
		&lt;password&gt;password&lt;/password&gt;
	&lt;/settings&gt;
&lt;/wordpy&gt;</pre></div></div>
</pre>
<p>Now that you see how we can save a file in response to the File | Save menu command you can probably imagine how we will respond to the File | Save As command:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_file_save_as</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Let the user save the current file to a new location.</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-identifier">xml_file</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">Untitled</span><span class="hl-quotes">&quot;
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">xml_file</span><span class="hl-code"> != </span><span class="hl-reserved">None</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">xml_file</span><span class="hl-default"> = </span><span class="hl-identifier">os</span><span class="hl-default">.</span><span class="hl-identifier">path</span><span class="hl-default">.</span><span class="hl-identifier">basename</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">xml_file</span><span class="hl-brackets">)

	</span><span class="hl-identifier">xml_file</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">file_browse</span><span class="hl-brackets">(</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_SAVE</span><span class="hl-code">, </span><span class="hl-identifier">xml_file</span><span class="hl-brackets">)
	</span><span class="hl-comment">#If we have a xml_file
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">xml_file</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">xml_save_to_file</span><span class="hl-brackets">(</span><span class="hl-identifier">xml_file</span><span class="hl-brackets">))</span><span class="hl-default">:
			</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Allright it all worked! save the current file and
			set the title.</span><span class="hl-quotes">&quot;&quot;&quot;
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">xml_file</span><span class="hl-default"> = </span><span class="hl-identifier">xml_file
			self</span><span class="hl-default">.</span><span class="hl-identifier">set_window_title_from_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">xml_file</span><span class="hl-brackets">)</span></pre></div></div>
<p>You&#8217;ll notice that it&#8217;s very similar to the on_file_save() function except that we always browse for a file and we pass the name of the current file (if it exists) to the file_browse() function so that the use has a starting point for their save as action.</p>
<p>So that&#8217;s it for saving the next step is opening a saved file.</p>
<h3>Opening an save file</h3>
<p>Opening one of our xml files is very similar to saving them except instead of creating the xml document and each node, we need to loop through an existing document and load each node into our specific variables.  The first thing that we need to do is respond to the File | Open menu command:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_file_open</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:

	</span><span class="hl-identifier">xml_file</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">file_browse</span><span class="hl-brackets">(</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_OPEN</span><span class="hl-brackets">)
	</span><span class="hl-comment">#If we have a xml_file
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">xml_file</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">xml_load_from_file</span><span class="hl-brackets">(</span><span class="hl-identifier">xml_file</span><span class="hl-brackets">))</span><span class="hl-default">:
			</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Allright it all worked! save the current file and
			set the title.</span><span class="hl-quotes">&quot;&quot;&quot;
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">xml_file</span><span class="hl-default"> = </span><span class="hl-identifier">xml_file
			self</span><span class="hl-default">.</span><span class="hl-identifier">set_window_title_from_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">xml_file</span><span class="hl-brackets">)</span></pre></div></div>
<p>Pretty standard stuff, we let the user browse for an xml file, and if they don&#8217;t cancel the operation we then load the xml file.  If that is successful we save the path to the xml file and then set the windows title.  Not much happening here as most of the work is done in xml_load_from_file():</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">xml_load_from_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">xml_file</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Load a post from an xml file
	@param xml_file - string - path to file that
	we will load.
	@returns boolean - True success. False failure
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-comment">#Init return value
	</span><span class="hl-identifier">success</span><span class="hl-default"> = </span><span class="hl-reserved">False

	</span><span class="hl-comment">#Load the xml_file to a document
	</span><span class="hl-reserved">try</span><span class="hl-default">:
		</span><span class="hl-identifier">xml_document</span><span class="hl-default"> = </span><span class="hl-identifier">minidom</span><span class="hl-default">.</span><span class="hl-identifier">parse</span><span class="hl-brackets">(</span><span class="hl-identifier">xml_file</span><span class="hl-brackets">)
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">xml_document</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-identifier">success</span><span class="hl-default"> = </span><span class="hl-brackets">((</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">xml_load</span><span class="hl-brackets">(</span><span class="hl-identifier">xml_document</span><span class="hl-brackets">))
					</span><span class="hl-identifier">and </span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">BlogSettings</span><span class="hl-code">.</span><span class="hl-identifier">xml_load</span><span class="hl-brackets">(</span><span class="hl-identifier">xml_document</span><span class="hl-brackets">)))
	</span><span class="hl-reserved">except IOError</span><span class="hl-default">, </span><span class="hl-brackets">(</span><span class="hl-identifier">errno</span><span class="hl-code">, </span><span class="hl-identifier">strerror</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(
			</span><span class="hl-quotes">&quot;</span><span class="hl-string">Error loading post file(%s): %s</span><span class="hl-quotes">&quot;</span><span class="hl-code"> % </span><span class="hl-brackets">(</span><span class="hl-identifier">errno</span><span class="hl-code">, </span><span class="hl-identifier">strerror</span><span class="hl-brackets">))
	</span><span class="hl-reserved">except</span><span class="hl-default">:
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Error loading post file.</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-reserved">return </span><span class="hl-identifier">success</span></pre></div></div>
<p>In this function we create our xml.minidom.Document object by telling the minidom mdule to parse the xml file that was passed to the function.  If that does not succeed we catch the appropriate exceptions and show our error dialog top the user.  If the parsing the file does succeed we load the post and the blog settings from the XML document. WordPy.xml_load() loads the post from the xml document:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">xml_load</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">xml_document</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Load the current blog post from an xml document.
	@param xml_document - xml.dom.minidom.Document object -
	The xml document that we will load the post from.
	@returns boolean True - success.  False - Failure.</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-identifier">title_loaded</span><span class="hl-default"> = </span><span class="hl-reserved">False
	</span><span class="hl-identifier">text_loaded</span><span class="hl-default"> = </span><span class="hl-reserved">False

	</span><span class="hl-comment">#Loop through all child nodes of the root.
	</span><span class="hl-reserved">for </span><span class="hl-identifier">node </span><span class="hl-reserved">in </span><span class="hl-identifier">xml_document</span><span class="hl-default">.</span><span class="hl-identifier">documentElement</span><span class="hl-default">.</span><span class="hl-identifier">childNodes</span><span class="hl-default">:
		</span><span class="hl-comment">#We are looking for the post Node
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">node</span><span class="hl-code">.</span><span class="hl-identifier">nodeName</span><span class="hl-code"> == </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">_xml_tags</span><span class="hl-brackets">[</span><span class="hl-identifier">POST_TAG</span><span class="hl-brackets">])</span><span class="hl-default">:
			</span><span class="hl-comment"># Now loop through the post nodes children
			</span><span class="hl-reserved">for </span><span class="hl-identifier">item_node </span><span class="hl-reserved">in </span><span class="hl-identifier">node</span><span class="hl-default">.</span><span class="hl-identifier">childNodes</span><span class="hl-default">:
				</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">item_node</span><span class="hl-code">.</span><span class="hl-identifier">nodeName</span><span class="hl-code"> == </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">_xml_tags</span><span class="hl-brackets">[</span><span class="hl-identifier">TITLE_TAG</span><span class="hl-brackets">])</span><span class="hl-default">:
					</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Set the title, the firstChild in this case is
					the actual title text that we saved.</span><span class="hl-quotes">&quot;&quot;&quot;

					</span><span class="hl-comment">#Make sure it's not a blank string
					</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">item_node</span><span class="hl-code">.</span><span class="hl-identifier">firstChild</span><span class="hl-brackets">)</span><span class="hl-default">:
						</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">enTitle</span><span class="hl-default">.</span><span class="hl-identifier">set_text</span><span class="hl-brackets">(</span><span class="hl-identifier">item_node</span><span class="hl-code">.</span><span class="hl-identifier">firstChild</span><span class="hl-code">.</span><span class="hl-identifier">nodeValue</span><span class="hl-brackets">)
					</span><span class="hl-reserved">else</span><span class="hl-default">:
						</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">enTitle</span><span class="hl-default">.</span><span class="hl-identifier">set_text</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)
					</span><span class="hl-identifier">title_loaded</span><span class="hl-default"> = </span><span class="hl-reserved">True
				</span><span class="hl-identifier">elif </span><span class="hl-brackets">(</span><span class="hl-identifier">item_node</span><span class="hl-code">.</span><span class="hl-identifier">nodeName</span><span class="hl-code"> == </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">_xml_tags</span><span class="hl-brackets">[</span><span class="hl-identifier">TEXT_TAG</span><span class="hl-brackets">])</span><span class="hl-default">:
					</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Set the text, the firstChild in this case is
					the actual text that we saved.</span><span class="hl-quotes">&quot;&quot;&quot;
					</span><span class="hl-comment">#Make sure it's not a blank string
					</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">item_node</span><span class="hl-code">.</span><span class="hl-identifier">firstChild</span><span class="hl-brackets">)</span><span class="hl-default">:
						</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">set_post_text</span><span class="hl-brackets">(</span><span class="hl-identifier">item_node</span><span class="hl-code">.</span><span class="hl-identifier">firstChild</span><span class="hl-code">.</span><span class="hl-identifier">nodeValue</span><span class="hl-brackets">)
					</span><span class="hl-reserved">else</span><span class="hl-default">:
						</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">set_post_text</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)
					</span><span class="hl-identifier">text_loaded</span><span class="hl-default"> = </span><span class="hl-reserved">True
			</span><span class="hl-comment">#Break out of the topmost for loop
			</span><span class="hl-reserved">break

	</span><span class="hl-identifier">return </span><span class="hl-brackets">(</span><span class="hl-identifier">title_loaded </span><span class="hl-reserved">and </span><span class="hl-identifier">text_loaded</span><span class="hl-brackets">)</span></pre></div></div>
<p>Basically what we do when loading our values is loop through the all of the child nodes of the root node looking for nodes that match whatever criteria we are looking for.  So in the xml example file:</p>
<pre>
<div class="hl-surround" ><div class="hl-main"><pre>&lt;wordpy&gt;
	&lt;post&gt;
		&lt;title&gt;My Blog Post&lt;/title&gt;
		&lt;text&gt;Here is my text&lt;/text&gt;
	&lt;/post&gt;
	&lt;settings&gt;
		&lt;url&gt;http://www.myblog.com/wordpress/xmlrpc.php&lt;/url&gt;
		&lt;username&gt;user&lt;/username&gt;
		&lt;password&gt;password&lt;/password&gt;
	&lt;/settings&gt;
&lt;/wordpy&gt;</pre></div></div>
</pre>
<p>The root node or the documentElement item, is the &lt;wordpy&gt; element.  So if we were to loop through its child nodes the first node that we would encounter would be the &lt;post&gt; node and then next node would be the &lt;settings&gt; node.  Then if we looped through he &lt;post&gt; elements children we would encounter the &lt;title&gt; and the &lt;text&gt; node.  The sole child node of either of those two nodes happens to be their actual text.</p>
<p>After explaining that it should be pretty obvious what is happening in the WordPy.xml_load() function.  Basically we loop through all the children of the root node looking for the &lt;post&gt; node.  Once we find it we then loop through all of the  &lt;post&gt; node&#8217;s children looking for the &lt;title&gt; and the &lt;text&gt; node.  When we encounter either the &lt;title&gt; node or the &lt;text&gt; node we get the the nodeValue of the firstChild, if it exists.  The firstChild of either of those two nodes happens to be the actual text node that we want to load.  The nodeValue of the text nodes is their actual text.  If firstChild did not exist it means that a blank string was saved into the text file.</p>
<p>For me the xml metaphoer built up by the xml.dom slightly of falls apart when then actual text is considered another node, but the implementation is simple and works well enough so I shouldn&#8217;t complain. (Plus there is probably a very good reason for this)</p>
<p>We also set boolean values to True to make sure that we know whether or not all of the proper elements were loaded.  This is an optional step as you might not care if everything was loaded properly..</p>
<p>The BlogSettings.xml_load() function does the exact same thing except that it looks for different nodes, as a result I will show it but I won&#8217;t bother explaining it.</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">xml_load</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">xml_document</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Load the current blog post from an xml document.
	@param xml_document - xml.dom.minidom.Document object -
	The xml document that we will load the post from.
	@returns boolean True - success.  False - Failure.</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-identifier">URL_loaded</span><span class="hl-default"> = </span><span class="hl-reserved">False
	</span><span class="hl-identifier">username_loaded</span><span class="hl-default"> = </span><span class="hl-reserved">False
	</span><span class="hl-identifier">password_loaded</span><span class="hl-default"> = </span><span class="hl-reserved">False

	</span><span class="hl-comment">#Loop through all child nodes of the root.
	</span><span class="hl-reserved">for </span><span class="hl-identifier">node </span><span class="hl-reserved">in </span><span class="hl-identifier">xml_document</span><span class="hl-default">.</span><span class="hl-identifier">documentElement</span><span class="hl-default">.</span><span class="hl-identifier">childNodes</span><span class="hl-default">:
		</span><span class="hl-comment">#We are looking for the post Node
		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">node</span><span class="hl-code">.</span><span class="hl-identifier">nodeName</span><span class="hl-code"> == </span><span class="hl-identifier">WordPy</span><span class="hl-code">.</span><span class="hl-identifier">_xml_tags</span><span class="hl-brackets">[</span><span class="hl-identifier">SETTINGS_TAG</span><span class="hl-brackets">])</span><span class="hl-default">:
			</span><span class="hl-comment"># Now loop through the post nodes children
			</span><span class="hl-reserved">for </span><span class="hl-identifier">item_node </span><span class="hl-reserved">in </span><span class="hl-identifier">node</span><span class="hl-default">.</span><span class="hl-identifier">childNodes</span><span class="hl-default">:
				</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">item_node</span><span class="hl-code">.</span><span class="hl-identifier">nodeName</span><span class="hl-code"> == </span><span class="hl-identifier">WordPy</span><span class="hl-code">.</span><span class="hl-identifier">_xml_tags</span><span class="hl-brackets">[</span><span class="hl-identifier">URL_TAG</span><span class="hl-brackets">])</span><span class="hl-default">:
					</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Set the URL</span><span class="hl-quotes">&quot;&quot;&quot;
					</span><span class="hl-comment">#Make sure it's not a blank string
					</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">item_node</span><span class="hl-code">.</span><span class="hl-identifier">firstChild</span><span class="hl-brackets">)</span><span class="hl-default">:
						</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">URL</span><span class="hl-default"> = </span><span class="hl-identifier">item_node</span><span class="hl-default">.</span><span class="hl-identifier">firstChild</span><span class="hl-default">.</span><span class="hl-identifier">nodeValue
					</span><span class="hl-reserved">else</span><span class="hl-default">:
						</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">URL</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;&quot;
					</span><span class="hl-identifier">URL_loaded</span><span class="hl-default"> = </span><span class="hl-reserved">True
				</span><span class="hl-identifier">elif </span><span class="hl-brackets">(</span><span class="hl-identifier">item_node</span><span class="hl-code">.</span><span class="hl-identifier">nodeName</span><span class="hl-code"> == </span><span class="hl-identifier">WordPy</span><span class="hl-code">.</span><span class="hl-identifier">_xml_tags</span><span class="hl-brackets">[</span><span class="hl-identifier">USERNAME_TAG</span><span class="hl-brackets">])</span><span class="hl-default">:
					</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Set the username</span><span class="hl-quotes">&quot;&quot;&quot;
					</span><span class="hl-comment">#Make sure it's not a blank string
					</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">item_node</span><span class="hl-code">.</span><span class="hl-identifier">firstChild</span><span class="hl-brackets">)</span><span class="hl-default">:
						</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">Username</span><span class="hl-default"> = </span><span class="hl-identifier">item_node</span><span class="hl-default">.</span><span class="hl-identifier">firstChild</span><span class="hl-default">.</span><span class="hl-identifier">nodeValue
					</span><span class="hl-reserved">else</span><span class="hl-default">:
						</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">Username</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;&quot;
					</span><span class="hl-identifier">username_loaded</span><span class="hl-default"> = </span><span class="hl-reserved">True
				</span><span class="hl-identifier">elif </span><span class="hl-brackets">(</span><span class="hl-identifier">item_node</span><span class="hl-code">.</span><span class="hl-identifier">nodeName</span><span class="hl-code"> == </span><span class="hl-identifier">WordPy</span><span class="hl-code">.</span><span class="hl-identifier">_xml_tags</span><span class="hl-brackets">[</span><span class="hl-identifier">PASSWORD_TAG</span><span class="hl-brackets">])</span><span class="hl-default">:
					</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Set the pasword</span><span class="hl-quotes">&quot;&quot;&quot;
					</span><span class="hl-comment">#Make sure it's not a blank string
					</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">item_node</span><span class="hl-code">.</span><span class="hl-identifier">firstChild</span><span class="hl-brackets">)</span><span class="hl-default">:
						</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">Password</span><span class="hl-default"> = </span><span class="hl-identifier">item_node</span><span class="hl-default">.</span><span class="hl-identifier">firstChild</span><span class="hl-default">.</span><span class="hl-identifier">nodeValue
					</span><span class="hl-reserved">else</span><span class="hl-default">:
						</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">Password</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;&quot;
					</span><span class="hl-identifier">password_loaded</span><span class="hl-default"> = </span><span class="hl-reserved">True
		</span><span class="hl-comment">#Break out of the topmost for loop
		</span><span class="hl-reserved">break

	</span><span class="hl-identifier">return </span><span class="hl-brackets">(</span><span class="hl-identifier">URL_loaded </span><span class="hl-reserved">and </span><span class="hl-identifier">username_loaded </span><span class="hl-reserved">and </span><span class="hl-identifier">password_loaded</span><span class="hl-brackets">)</span></pre></div></div>
<h3>Starting a new file</h3>
<p>All that&#8217;s left now is to handle the File | New menu command:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_file_new</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">enTitle</span><span class="hl-default">.</span><span class="hl-identifier">set_text</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">set_post_text</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">xml_file</span><span class="hl-default"> = </span><span class="hl-reserved">None
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">set_window_title_from_file</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">xml_file</span><span class="hl-brackets">)</span></pre></div></div>
<p>It doesn&#8217;t really do much except blank out the title and text, make it so that there is no xml file, and sets the window title to &#8220;Wordpy – Untitled&#8221; (which is what set_window_title will do when passed None as the xml file.)</p>
<p>Since this is a good way to initialize WordPy I call it in the __init__ function:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-comment">#initialize to new
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_file_new</span><span class="hl-brackets">(</span><span class="hl-reserved">None</span><span class="hl-brackets">)</span></pre></div></div>
<h2>The End</h2>
<p>You can download the complete code for this tutorial <a href="http://www.learningpython.com/sources/WordPy_0_2.tar.gz">here</a>.</p>
<p>Well that&#8217;s it for this lesson on saving and loading data from an xml file, there are still many more things that need to be done to WordPy, like figuring out what the best way to handle the blog settings are since I&#8217;ve never been happy with the way that they were handled in the previous tutorial.  For example  should they be separate from the post&#8217;s when you save them?  Should there be a list of blog settings?  Should you have to log into your blog first?  I&#8217;m not sure yet but those will be left for another post since this one was simply designed to show one method for saving and loading data from xml files using python.</p>
<p>As always if you have any questions or notice any problems please leave a comment!</p>
<div style="float:right;margin:0px 0px 0px 0px;"><a href="http://www.google.com/reader/link?url=http://www.learningpython.com/2006/10/24/wordpy-02-using-xml-to-save-and-load-data/&title=WordPy 0.2 - Using XML to Save and Load Data&srcTitle=learning python&srcURL=http://www.learningpython.com"target="_blank" rel=""><img border="0" src="http://www.learningpython.com/wp-content/plugins/wp-google-buzz/icon/12.png" style="opacity:1;filter:alpha(opacity=100)" onmouseover="this.style.opacity=0.8;this.filters.alpha.opacity=70" onmouseout="this.style.opacity=1;this.filters.alpha.opacity=100"/> </a></div>]]></content:encoded>
			<wfw:commentRss>http://www.learningpython.com/2006/10/24/wordpy-02-using-xml-to-save-and-load-data/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Extending our PyGTK Application</title>
		<link>http://www.learningpython.com/2006/09/02/extending-our-pygtk-application/</link>
		<comments>http://www.learningpython.com/2006/09/02/extending-our-pygtk-application/#comments</comments>
		<pubDate>Sat, 02 Sep 2006 19:42:00 +0000</pubDate>
		<dc:creator>selsine</dc:creator>
				<category><![CDATA[PyGTK]]></category>
		<category><![CDATA[glade]]></category>
		<category><![CDATA[gui]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://www.learningpython.com/?p=39</guid>
		<description><![CDATA[In this tutorial we are going to extend our <a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">PyWine</a> application to allowing you to edit the items that you have added to the list and save an load the wine lists you create so that you don't have to keep entering them in all the time.]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.learningpython.com%2F2006%2F09%2F02%2Fextending-our-pygtk-application%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.learningpython.com%2F2006%2F09%2F02%2Fextending-our-pygtk-application%2F&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<p>In this tutorial we are going to extend our <a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">PyWine</a> application to allowing you to edit the items that you have added to the list and save an load the wine lists you create so that you don&#8217;t have to keep entering them in all the time.</p>
<p>You can download the complete code for this tutorial <a href="http://www.learningpython.com/sources/PyWine_02.tar.gz">here</a>.</p>
<p>If you are not familiar with the PyWine application or with working with Glade and PyGTK I suggest you read my first two tutorial on the subject:</p>
<ul>
<li><a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">Building an Application with PyGTK and Glade</a></li>
<li><a href="http://www.learningpython.com/2006/05/07/creating-a-gui-using-pygtk-and-glade/">Creating a GUI using PyGTK and Glade</a></li>
</ul>
<h2>The GUI &#8211; Glade</h2>
<p>The first thing that we are going to do is open up out PyWine glad project and add an edit button to our toolbar:</p>
<ol>
<li>Make space for the Edit button by selecting the toolbar and settings its size property to 2.</li>
<li>Add a Toolbar Button in the newly created empty space.</li>
<li>Call the button btnEditWine, set its label to be &#8220;Edit Wine&#8221;, and its icon to be the stock Edit icon.  Next add a handler for the clicked event.</li>
<li>We&#8217;re going to change the menu up a little bit, instead of a menu that says Add | Wine, we are going to set the menu up to read Wine | Add and Wine | Edit.  Do this just like we did in the previous PyWine tutorial and make the Wine | Edit clicked handler the same function as your btnEditWine button&#8217;s clicked event handler.</li>
</ol>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/py_wine_02/py_wine_02_01.png" alt="GLADE Window PyWine" border="0" /></p>
<p><span id="more-39"></span></p>
<h2>The Code</h2>
<p>Now lets get the Edit button working in the code, the first thing that we are going to have to do is get the information from whatever line in the <a href="http://www.pygtk.org/pygtk2reference/class-gtktreeview.html">gtk.TreeView</a> is currently selected.  There are two ways to go about his, the first way is to read all the of data from the four columns that we have visible and the second would be to actually add our Wine object to the <a href="http://www.pygtk.org/pygtk2reference/class-gtkliststore.html">gtk.ListStore</a> (our model) but not display it in the gtk.TreeView.</p>
<p>Since it is simpler and may be more useful in the future if our wine class contains extra information or if we choose to let people add or remove columns from the gtk.TreeView I choose to use the later approach.  This means that we need to change some of our code a little but.</p>
<p>First we have to add an additional column to our column definition variables in the pyWine __init__ function:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">cWineObject</span><span class="hl-default"> = </span><span class="hl-number">0
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">cWine</span><span class="hl-default"> = </span><span class="hl-number">1
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">cWinery</span><span class="hl-default"> = </span><span class="hl-number">2
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">cGrape</span><span class="hl-default"> = </span><span class="hl-number">3
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">cYear</span><span class="hl-default"> = </span><span class="hl-number">4</span></pre></div></div>
<p>You see that we put the actual wine object as position 0 in the list, so we have to adjust our gtk.ListStore creation code in the same function as follows:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-comment">#Create the listStore Model to use with the wineView
</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wineList</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">ListStore</span><span class="hl-brackets">(</span><span class="hl-identifier">gobject</span><span class="hl-code">.</span><span class="hl-identifier">TYPE_PYOBJECT</span><span class="hl-code">
							, </span><span class="hl-identifier">gobject</span><span class="hl-code">.</span><span class="hl-identifier">TYPE_STRING</span><span class="hl-code">
							, </span><span class="hl-identifier">gobject</span><span class="hl-code">.</span><span class="hl-identifier">TYPE_STRING</span><span class="hl-code">
							, </span><span class="hl-identifier">gobject</span><span class="hl-code">.</span><span class="hl-identifier">TYPE_STRING</span><span class="hl-code">
							, </span><span class="hl-identifier">gobject</span><span class="hl-code">.</span><span class="hl-identifier">TYPE_STRING</span><span class="hl-brackets">)</span></pre></div></div>
<p>Everything is the same as it was before except that the first item in our gtk.ListStore will now be a python object.  In order to get the above code to compile we must add the following code to the top of our file: </p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre><span class="hl-reserved">import </span><span class="hl-identifier">gobject</span></pre></div></div>
<p>Now the next thing that we need to change the way that we add our wine to the gtk.ListStore so that we actually include the Wine object.  Luckily in our previous tutorial we added a  getList() function to our Wine class that returns the list to add to the gtk.ListStore(), so all we have to do is edit that:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">getList</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function returns a list made up of the
	wine information.  It is used to add a wine to the
	wineList easily</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-reserved">return </span><span class="hl-brackets">[</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">wine</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">winery</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">grape</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">year</span><span class="hl-brackets">]</span></pre></div></div>
<p>It&#8217;s not much of a change, we simple have to make it so that getList() puts the Wine class at the start of the list.</p>
<p>The next step is to actually allow the user to edit a wine entry but before we do that there is one more change that we need to make.  In tutorial one the __init__ function of the wine dialog accepted all of the items that made up our wine class as initialization parameters.</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">wine</span><span class="hl-code">=</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-code">, </span><span class="hl-identifier">winery</span><span class="hl-code">=</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-code">, </span><span class="hl-identifier">grape</span><span class="hl-code">=</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-code">, </span><span class="hl-identifier">year</span><span class="hl-code">=</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)</span><span class="hl-default">:</span></pre></div></div>
<p>This works alright if you have a small number or parameters, but if our Wine class was going to grow initializing the wineDialog class would become a pain.  So all we are going to do is change the __init__ function to accept a Wine class object, rather then all of its parts:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">wine</span><span class="hl-code">=</span><span class="hl-reserved">None</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Initialize the class.
	wine - a Wine object</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-comment">#setup the glade file
	</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">gladefile</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">pywine.glade</span><span class="hl-quotes">&quot;
	</span><span class="hl-comment">#setup the wine that we will return
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">wine</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-comment">#They have passed a wine object
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wine</span><span class="hl-default"> = </span><span class="hl-identifier">wine
	</span><span class="hl-reserved">else</span><span class="hl-default">:
		</span><span class="hl-comment">#Just use a blank wine
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wine</span><span class="hl-default"> = </span><span class="hl-identifier">Wine</span><span class="hl-brackets">()</span></pre></div></div>
<p>The next step is to finally edit a wine entry, we will do this in function called on_EditWine(), it is hooked up to the Edit Wine button clicked event and the Wine | Edit menu item:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_EditWine</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the user wants to edit a wine entry</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-comment"># Get the selection in the gtk.TreeView
	</span><span class="hl-identifier">selection</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wineView</span><span class="hl-default">.</span><span class="hl-identifier">get_selection</span><span class="hl-brackets">()
	</span><span class="hl-comment"># Get the selection iter
	</span><span class="hl-identifier">model</span><span class="hl-default">, </span><span class="hl-identifier">selection_iter</span><span class="hl-default"> = </span><span class="hl-identifier">selection</span><span class="hl-default">.</span><span class="hl-identifier">get_selected</span><span class="hl-brackets">()

	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">selection_iter</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">There is a selection, so now get the the value at column
		self.cWineObject, the Wine Object</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">wine</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wineList</span><span class="hl-default">.</span><span class="hl-identifier">get_value</span><span class="hl-brackets">(</span><span class="hl-identifier">selection_iter</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">cWineObject</span><span class="hl-brackets">)
		</span><span class="hl-comment"># Create the wine dialog, based off of the current selection
		</span><span class="hl-identifier">wineDlg</span><span class="hl-default"> = </span><span class="hl-identifier">wineDialog</span><span class="hl-brackets">(</span><span class="hl-identifier">wine</span><span class="hl-brackets">)</span><span class="hl-default">;
		</span><span class="hl-identifier">result</span><span class="hl-default">,</span><span class="hl-identifier">newWine</span><span class="hl-default"> = </span><span class="hl-identifier">wineDlg</span><span class="hl-default">.</span><span class="hl-identifier">run</span><span class="hl-brackets">()

		</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">result</span><span class="hl-code"> == </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_OK</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">The user clicked Ok, so let's save the changes back
			into the gtk.ListStore</span><span class="hl-quotes">&quot;&quot;&quot;
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wineList</span><span class="hl-default">.</span><span class="hl-identifier">set</span><span class="hl-brackets">(</span><span class="hl-identifier">selection_iter</span><span class="hl-code">
					, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">cWineObject</span><span class="hl-code">, </span><span class="hl-identifier">newWine</span><span class="hl-code">
					, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">cWine</span><span class="hl-code">, </span><span class="hl-identifier">newWine</span><span class="hl-code">.</span><span class="hl-identifier">wine</span><span class="hl-code">
					, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">cWinery</span><span class="hl-code">, </span><span class="hl-identifier">newWine</span><span class="hl-code">.</span><span class="hl-identifier">winery</span><span class="hl-code">
					, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">cGrape</span><span class="hl-code">, </span><span class="hl-identifier">newWine</span><span class="hl-code">.</span><span class="hl-identifier">grape</span><span class="hl-code">
					, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">cYear</span><span class="hl-code">, </span><span class="hl-identifier">newWine</span><span class="hl-code">.</span><span class="hl-identifier">year</span><span class="hl-brackets">)</span></pre></div></div>
<p>The first thing we do is call <a href="http://www.pygtk.org/pygtk2reference/class-gtktreeview.html#method-gtktreeview--get-selection">gtk.TreeView.get_selection()</a> to get the <a href="http://www.pygtk.org/pygtk2reference/class-gtktreeselection.html">gtk.TreeSelection</a> object that is associated with the gtk.TreeView.  Then we call <a href="http://www.pygtk.org/pygtk2reference/class-gtktreeselection.html#method-gtktreeselection--get-selected">gtk.TreeSelection.get_selected()</a> which returns our <a href="http://www.pygtk.org/pygtk2reference/class-gtktreemodel.html">gtk.TreeModel</a> (which we don&#8217;t care about) and a <a href="http://www.pygtk.org/pygtk2reference/class-gtktreeiter.html">gtk.TreeIter</a> that points to the currently selected node in out gtk.TreeView (which we do care about).</p>
<p>The gtk.TreeIter returned by the get_selected() function will be None if there is no selection, otherwise we use that gtk.TreeIter to get the Wine object at the currently selected position in our gtk.TreeView by calling the <a href="http://www.pygtk.org/pygtk2reference/class-gtktreemodel.html#method-gtktreemodel--get-value">gtk.TreeModel.get_value()</a> action.  Once we have the Wine object the rest is pretty straight forward, we create our wineDialog object, show it, and if the used clicks the Ok button we update the selected item in the gtk.TreeView using the <a href="http://www.pygtk.org/pygtk2reference/class-gtkliststore.html#method-gtkliststore--set">gtk.ListStore.set()</a> function.</p>
<p>The <a href="http://www.pygtk.org/pygtk2reference/class-gtkliststore.html#method-gtkliststore--set">gtk.ListStore.set()</a> function is actually quite interesting since it takes a gtk.TreeIter as its first parameter (the position to set the values) and the rest of its parameters can be one or more column_number, new_value pairs!   My only disappointment was not finding a function that used a list in the same way that the <a href="http://www.pygtk.org/pygtk2reference/class-gtkliststore.html#method-gtkliststore--append">gtk.ListStore.append()</a> function does.</p>
<p>So that&#8217;s it for editing a wine entry!  Since we don&#8217;t want to always re-enter the wines that we like each time we start the application it&#8217;s high time that we start saving and loading our wine list.</p>
<h2>Saving and Loading the Wine Lists</h2>
<p>The first thing that we are going to do is borrow two helper functions from the <a href="http://www.learningpython.com/2006/08/19/wordpress-python-library/">WordPy offline blogging tool </a>tutorial:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">error_string</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This Function is used to show an error dialog when
	an error occurs.
	error_string - The error string that will be displayed
	on the dialog.
	</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">error_dlg</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">MessageDialog</span><span class="hl-brackets">(</span><span class="hl-identifier">type</span><span class="hl-code">=</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">MESSAGE_ERROR</span><span class="hl-code">
				, </span><span class="hl-identifier">message_format</span><span class="hl-code">=</span><span class="hl-identifier">error_string</span><span class="hl-code">
				, </span><span class="hl-identifier">buttons</span><span class="hl-code">=</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">BUTTONS_OK</span><span class="hl-brackets">)
	</span><span class="hl-identifier">error_dlg</span><span class="hl-default">.</span><span class="hl-identifier">run</span><span class="hl-brackets">()
	</span><span class="hl-identifier">error_dlg</span><span class="hl-default">.</span><span class="hl-identifier">destroy</span><span class="hl-brackets">()</span></pre></div></div>
<p>This function just provides us with an easy way to let the user know that an error has occurred by showing them an error dialog.  We will add this to the pyWine class.  For more information on how the function works please see the <a href="http://www.learningpython.com/2006/08/19/wordpress-python-library/">WordPy offline blogging tool</a> tutorial.</p>
<p>We are also going to bring over the browse_for_image() function:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">browse_for_image</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is used to browse for an image.
	The path to the image will be returned if the user
	selects one, however a blank string will be returned
	if they cancel or do not select one.</span><span class="hl-quotes">&quot;&quot;&quot;
	
	</span><span class="hl-identifier">file_open</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">FileChooserDialog</span><span class="hl-brackets">(</span><span class="hl-identifier">title</span><span class="hl-code">=</span><span class="hl-quotes">&quot;</span><span class="hl-string">Select Image</span><span class="hl-quotes">&quot;</span><span class="hl-code">
				, </span><span class="hl-identifier">action</span><span class="hl-code">=</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_OPEN</span><span class="hl-code">
				, </span><span class="hl-identifier">buttons</span><span class="hl-code">=</span><span class="hl-brackets">(</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">STOCK_CANCEL</span><span class="hl-code">
							, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_CANCEL</span><span class="hl-code">
							, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">STOCK_OPEN</span><span class="hl-code">
							, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_OK</span><span class="hl-brackets">))
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Create and add the Images filter</span><span class="hl-quotes">&quot;&quot;&quot;		
	</span><span class="hl-identifier">filter</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">FileFilter</span><span class="hl-brackets">()
	</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">set_name</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Images</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">add_mime_type</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">image/png</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">add_mime_type</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">image/jpeg</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">add_mime_type</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">image/gif</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">add_pattern</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">*.png</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">add_pattern</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">*.jpg</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">add_pattern</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">*.gif</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">file_open</span><span class="hl-default">.</span><span class="hl-identifier">add_filter</span><span class="hl-brackets">(</span><span class="hl-identifier">filter</span><span class="hl-brackets">)
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Create and add the 'all files' filter</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">filter</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">FileFilter</span><span class="hl-brackets">()
	</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">set_name</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">All files</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">add_pattern</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">*</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">file_open</span><span class="hl-default">.</span><span class="hl-identifier">add_filter</span><span class="hl-brackets">(</span><span class="hl-identifier">filter</span><span class="hl-brackets">)
	
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Init the return value</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">result</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;&quot;
	</span><span class="hl-reserved">if </span><span class="hl-identifier">file_open</span><span class="hl-default">.</span><span class="hl-identifier">run</span><span class="hl-brackets">()</span><span class="hl-default"> == </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">RESPONSE_OK</span><span class="hl-default">:
		</span><span class="hl-identifier">result</span><span class="hl-default"> = </span><span class="hl-identifier">file_open</span><span class="hl-default">.</span><span class="hl-identifier">get_filename</span><span class="hl-brackets">()
	</span><span class="hl-identifier">file_open</span><span class="hl-default">.</span><span class="hl-identifier">destroy</span><span class="hl-brackets">()
	
	</span><span class="hl-reserved">return </span><span class="hl-identifier">result</span></pre></div></div>
<p>Except we are going to modify it so that it operates as a File Open and File Save dialog, and so that it browses for pyWine files (*.pwi) instead of images.  We will control whether it is a File Open or File Save dialog by passing in an additional parameter called dialog_action, which will be the action that we use to set the action property of the <a href="http://www.pygtk.org/pygtk2reference/class-gtkfilechooserdialog.html">gtk.FileChooserDialog</a>:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">file_browse</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">dialog_action</span><span class="hl-code">, </span><span class="hl-identifier">file_name</span><span class="hl-code">=</span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This function is used to browse for a pyWine file.
	It can be either a save or open dialog depending on
	what dialog_action is.
	The path to the file will be returned if the user
	selects one, however a blank string will be returned
	if they cancel or do not select one.
	dialog_action - The open or save mode for the dialog either
	gtk.FILE_CHOOSER_ACTION_OPEN, gtk.FILE_CHOOSER_ACTION_SAVE
        file_name - Default name when doing a save</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">dialog_action</span><span class="hl-code">==</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_OPEN</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">dialog_buttons</span><span class="hl-default"> = </span><span class="hl-brackets">(</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">STOCK_CANCEL</span><span class="hl-code">
							, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_CANCEL</span><span class="hl-code">
							, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">STOCK_OPEN</span><span class="hl-code">
							, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_OK</span><span class="hl-brackets">)
	</span><span class="hl-reserved">else</span><span class="hl-default">:
		</span><span class="hl-identifier">dialog_buttons</span><span class="hl-default"> = </span><span class="hl-brackets">(</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">STOCK_CANCEL</span><span class="hl-code">
							, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_CANCEL</span><span class="hl-code">
							, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">STOCK_SAVE</span><span class="hl-code">
							, </span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">RESPONSE_OK</span><span class="hl-brackets">)

	</span><span class="hl-identifier">file_dialog</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">FileChooserDialog</span><span class="hl-brackets">(</span><span class="hl-identifier">title</span><span class="hl-code">=</span><span class="hl-quotes">&quot;</span><span class="hl-string">Select Project</span><span class="hl-quotes">&quot;</span><span class="hl-code">
				, </span><span class="hl-identifier">action</span><span class="hl-code">=</span><span class="hl-identifier">dialog_action</span><span class="hl-code">
				, </span><span class="hl-identifier">buttons</span><span class="hl-code">=</span><span class="hl-identifier">dialog_buttons</span><span class="hl-brackets">)
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">set the filename if we are saving</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">dialog_action</span><span class="hl-code">==</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_SAVE</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-identifier">file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">set_current_name</span><span class="hl-brackets">(</span><span class="hl-identifier">file_name</span><span class="hl-brackets">)
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Create and add the pywine filter</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">filter</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">FileFilter</span><span class="hl-brackets">()
	</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">set_name</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">pyWine database</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">add_pattern</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">*.</span><span class="hl-quotes">&quot;</span><span class="hl-code"> + </span><span class="hl-identifier">FILE_EXT</span><span class="hl-brackets">)
	</span><span class="hl-identifier">file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">add_filter</span><span class="hl-brackets">(</span><span class="hl-identifier">filter</span><span class="hl-brackets">)
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Create and add the 'all files' filter</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">filter</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">FileFilter</span><span class="hl-brackets">()
	</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">set_name</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">All files</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">filter</span><span class="hl-default">.</span><span class="hl-identifier">add_pattern</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">*</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
	</span><span class="hl-identifier">file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">add_filter</span><span class="hl-brackets">(</span><span class="hl-identifier">filter</span><span class="hl-brackets">)

	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Init the return value</span><span class="hl-quotes">&quot;&quot;&quot;
	</span><span class="hl-identifier">result</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;&quot;
	</span><span class="hl-reserved">if </span><span class="hl-identifier">file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">run</span><span class="hl-brackets">()</span><span class="hl-default"> == </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">RESPONSE_OK</span><span class="hl-default">:
		</span><span class="hl-identifier">result</span><span class="hl-default"> = </span><span class="hl-identifier">file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">get_filename</span><span class="hl-brackets">()
	</span><span class="hl-identifier">file_dialog</span><span class="hl-default">.</span><span class="hl-identifier">destroy</span><span class="hl-brackets">()

	</span><span class="hl-reserved">return </span><span class="hl-identifier">result</span></pre></div></div>
<p>FILE_EXT is simply defined as follows:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre><span class="hl-identifier">FILE_EXT</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">pwi</span><span class="hl-quotes">&quot;</span></pre></div></div>
<p>We are also going to want to add handlers for the File | Open and File | Save menu commands in our glad project using the same method that we did for the Wine | Add and Wine | Edit menu items.  I called mine on_file_open and on_file_save:</p>
<div class="hl-surround" ><div class="hl-main"><pre>#Create our dictionay and connect it
dic = {&quot;on_mainWindow_destroy&quot; : self.on_Quit
		, &quot;on_AddWine&quot; : self.OnAddWine
		, &quot;on_EditWine&quot; : self.on_EditWine
		, &quot;on_file_open&quot; : self.on_file_open
		, &quot;on_file_save&quot; : self.on_file_save}
self.wTree.signal_autoconnect(dic)</pre></div></div>
<p>To do the saving and loading of our Wine objects we are going to use the <a href="http://docs.python.org/lib/module-shelve.html">python shelve</a> module.  Which is a standard python module that can most (if not all) python object.  There are of course other ways that we could have done this, we could have used xml files, or a straight pickle of all of our objects, but I thought that shelve made sense in this situation and  it is easier to introduce them xml saving and loading.</p>
<p>From the documentation:</p>
<blockquote><p>A &#8220;shelf&#8221; is a persistent, dictionary-like object. The difference with &#8220;dbm&#8221; databases is that the values (not the keys!) in a shelf can be essentially arbitrary Python objects &#8212; anything that the pickle module can handle. This includes most class instances, recursive data types, and objects containing lots of shared sub-objects. The keys are ordinary strings.</p></blockquote>
<h3>Saving</h3>
<p>So to start lets setup the on_file_save() function.  To start we will let the user browse for a location where they want to save their file, and let them specify the file&#8217;s name.  Next we will ensure that that the file has our file extension on it and then we will loop through all the items in the gtk.TreeView and save each Wine object using the shelve module:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_file_save</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the user wants to save a wine list</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-comment"># Get the File Save path
	</span><span class="hl-identifier">save_file</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">file_browse</span><span class="hl-brackets">(</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_SAVE</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">project_file</span><span class="hl-brackets">)
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">save_file</span><span class="hl-code"> != </span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-comment"># We have a path, ensure the proper extension
		</span><span class="hl-identifier">save_file</span><span class="hl-default">, </span><span class="hl-identifier">extension</span><span class="hl-default"> = </span><span class="hl-identifier">os</span><span class="hl-default">.</span><span class="hl-identifier">path</span><span class="hl-default">.</span><span class="hl-identifier">splitext</span><span class="hl-brackets">(</span><span class="hl-identifier">save_file</span><span class="hl-brackets">)
		</span><span class="hl-identifier">save_file</span><span class="hl-default"> = </span><span class="hl-identifier">save_file</span><span class="hl-default"> + </span><span class="hl-quotes">&quot;</span><span class="hl-string">.</span><span class="hl-quotes">&quot;</span><span class="hl-default"> + </span><span class="hl-identifier">FILE_EXT
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string"> Now we have the &quot;real&quot; file save loction create
		the shelve file, use &quot;n&quot; to create a new file</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">db</span><span class="hl-default"> = </span><span class="hl-identifier">shelve</span><span class="hl-default">.</span><span class="hl-builtin">open</span><span class="hl-brackets">(</span><span class="hl-identifier">save_file</span><span class="hl-code">,</span><span class="hl-quotes">&quot;</span><span class="hl-string">n</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Get the first item in the gtk.ListStore, and while it is not
		None, move forward through the list saving each item</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-comment"># Get the first item in the list
		</span><span class="hl-identifier">iter</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wineList</span><span class="hl-default">.</span><span class="hl-identifier">get_iter_root</span><span class="hl-brackets">()
		</span><span class="hl-identifier">while </span><span class="hl-brackets">(</span><span class="hl-identifier">iter</span><span class="hl-brackets">)</span><span class="hl-default">:
			</span><span class="hl-comment"># Get the wine at the current gtk.TreeIter
			</span><span class="hl-identifier">wine</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wineList</span><span class="hl-default">.</span><span class="hl-identifier">get_value</span><span class="hl-brackets">(</span><span class="hl-identifier">iter</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">cWineObject</span><span class="hl-brackets">)
			</span><span class="hl-comment"># Use the iters position in the list as the key name
			</span><span class="hl-identifier">db</span><span class="hl-brackets">[</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">wineList</span><span class="hl-code">.</span><span class="hl-identifier">get_string_from_iter</span><span class="hl-brackets">(</span><span class="hl-identifier">iter</span><span class="hl-brackets">)]</span><span class="hl-default"> = </span><span class="hl-identifier">wine
			</span><span class="hl-comment"># Get the next iter
			</span><span class="hl-identifier">iter</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wineList</span><span class="hl-default">.</span><span class="hl-identifier">iter_next</span><span class="hl-brackets">(</span><span class="hl-identifier">iter</span><span class="hl-brackets">)
		</span><span class="hl-comment">#close the database and write changes to disk, we are done
		</span><span class="hl-identifier">db</span><span class="hl-default">.</span><span class="hl-identifier">close</span><span class="hl-brackets">()</span><span class="hl-default">;
		</span><span class="hl-comment">#set the project file
		</span><span class="hl-identifier">root</span><span class="hl-default">, </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">project_file</span><span class="hl-default"> = </span><span class="hl-identifier">os</span><span class="hl-default">.</span><span class="hl-identifier">path</span><span class="hl-default">.</span><span class="hl-identifier">split</span><span class="hl-brackets">(</span><span class="hl-identifier">save_file</span><span class="hl-brackets">)</span></pre></div></div>
<p>After working with the <a href="http://www.pygtk.org/pygtk2reference/class-gtktreeiter.html">gtk.TreeIter</a> objects earlier this code should not be that difficult to understand.  In fact the only real difficult part of the code is the following, the rest should be explained by the in-line comments:</p>
<div class="hl-surround" ><div class="hl-main"><pre><span class="hl-identifier">while </span><span class="hl-brackets">(</span><span class="hl-identifier">iter</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-comment"># Get the wine at the current gtk.TreeIter
	</span><span class="hl-identifier">wine</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wineList</span><span class="hl-default">.</span><span class="hl-identifier">get_value</span><span class="hl-brackets">(</span><span class="hl-identifier">iter</span><span class="hl-code">, </span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">cWineObject</span><span class="hl-brackets">)
	</span><span class="hl-comment"># Use the iters position in the list as the key name
	</span><span class="hl-identifier">db</span><span class="hl-brackets">[</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">wineList</span><span class="hl-code">.</span><span class="hl-identifier">get_string_from_iter</span><span class="hl-brackets">(</span><span class="hl-identifier">iter</span><span class="hl-brackets">)]</span><span class="hl-default"> = </span><span class="hl-identifier">wine
	</span><span class="hl-comment"># Get the next iter
	</span><span class="hl-identifier">iter</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wineList</span><span class="hl-default">.</span><span class="hl-identifier">iter_next</span><span class="hl-brackets">(</span><span class="hl-identifier">iter</span><span class="hl-brackets">)</span></pre></div></div>
<p>Basically what we are doing is looping through each item in the gtk.ListStore and then setting the data at the current gtk.TreeIter position in the shelve file to be our wine object.</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre><span class="hl-identifier">db</span><span class="hl-brackets">[</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">wineList</span><span class="hl-code">.</span><span class="hl-identifier">get_string_from_iter</span><span class="hl-brackets">(</span><span class="hl-identifier">iter</span><span class="hl-brackets">)]</span><span class="hl-default"> = </span><span class="hl-identifier">wine</span></pre></div></div>
<p>The <a href="http://www.pygtk.org/pygtk2reference/class-gtktreemodel.html#method-gtktreemodel--get-string-from-iter">gtk.TreeModel.get_string_from_iter()</a> function &#8220;returns a string representation of the path pointed to by iter. This string is a &#8216;:&#8217; separated list of numbers. For example, &#8220;4:10:0:3&#8243; would be an acceptable return value for this string.&#8221; (pyGTK Docs).  Since we are using a <a href="http://www.pygtk.org/pygtk2reference/class-gtkliststore.html#method-gtkliststore--clear">gtk.ListStore</a> the values returned will always be single values that increase as we move down the list from top to bottom.  </p>
<p>So the first item will be &#8220;0&#8243;, the second &#8220;1&#8243;, the third &#8220;2&#8243;, and so on.  This will be helpful for us when we open files, since keys in shelve files are not guaranteed to be in any particular order (as far as I could tell.)</p>
<p>When you close the shelve file, the data will be written to the disk.</p>
<p>You&#8217;ll also notice the inclusion of the self.project_file item as the default file name, this is a new addition to the class.  It is the file name of the current project, it just lets us set the default name in the gtk.FileChooserDialog when we are doing a save.  It&#8217;s defined in the __init__ function as follows:</p>
<p><code><br />
self.project_file = ""<br />
</code></p>
<p>This just lets us have a dialog that pops up like so: </p>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/py_wine_02/py_wine_02_02.png" alt="GLADE Window PyWine" border="0" /></p>
<h2>Loading</h2>
<p>Now lets setup the on_file_open() function which, if you&#8217;ve understood the on_file_save function, should not be that difficult to understand:</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-reserved">def </span><span class="hl-identifier">on_file_open</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the user wants to open a wine</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-comment"># Get the file to open
	</span><span class="hl-identifier">open_file</span><span class="hl-default"> = </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">file_browse</span><span class="hl-brackets">(</span><span class="hl-identifier">gtk</span><span class="hl-code">.</span><span class="hl-identifier">FILE_CHOOSER_ACTION_OPEN</span><span class="hl-brackets">)
	</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">open_file</span><span class="hl-code"> != </span><span class="hl-quotes">&quot;&quot;</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-comment"># We have a path, open it for reading
		</span><span class="hl-reserved">try</span><span class="hl-default">:
			</span><span class="hl-identifier">db</span><span class="hl-default"> = </span><span class="hl-identifier">shelve</span><span class="hl-default">.</span><span class="hl-builtin">open</span><span class="hl-brackets">(</span><span class="hl-identifier">open_file</span><span class="hl-code">,</span><span class="hl-quotes">&quot;</span><span class="hl-string">r</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
			</span><span class="hl-identifier">if </span><span class="hl-brackets">(</span><span class="hl-identifier">db</span><span class="hl-brackets">)</span><span class="hl-default">:
				</span><span class="hl-comment"># We have opened the file, so empty out our gtk.TreeView
				</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wineList</span><span class="hl-default">.</span><span class="hl-identifier">clear</span><span class="hl-brackets">()
				</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string"> Since the shelve file is not gaurenteed to be in order we
				move through the file starting at iter 0 and moving our
				way up</span><span class="hl-quotes">&quot;&quot;&quot;
				</span><span class="hl-identifier">count</span><span class="hl-default"> = </span><span class="hl-number">0</span><span class="hl-default">;
				</span><span class="hl-reserved">while </span><span class="hl-identifier">db</span><span class="hl-default">.</span><span class="hl-identifier">has_key</span><span class="hl-brackets">(</span><span class="hl-builtin">str</span><span class="hl-brackets">(</span><span class="hl-identifier">count</span><span class="hl-brackets">))</span><span class="hl-default">:
					</span><span class="hl-identifier">newwine</span><span class="hl-default"> = </span><span class="hl-identifier">db</span><span class="hl-brackets">[</span><span class="hl-builtin">str</span><span class="hl-brackets">(</span><span class="hl-identifier">count</span><span class="hl-brackets">)]
					</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wineList</span><span class="hl-default">.</span><span class="hl-identifier">append</span><span class="hl-brackets">(</span><span class="hl-identifier">newwine</span><span class="hl-code">.</span><span class="hl-identifier">getList</span><span class="hl-brackets">())
					</span><span class="hl-identifier">count</span><span class="hl-default"> = </span><span class="hl-identifier">count</span><span class="hl-default"> +</span><span class="hl-number">1
				</span><span class="hl-identifier">db</span><span class="hl-default">.</span><span class="hl-identifier">close</span><span class="hl-brackets">()</span><span class="hl-default">;
				</span><span class="hl-comment">#set the project file
				</span><span class="hl-identifier">root</span><span class="hl-default">, </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">project_file</span><span class="hl-default"> = </span><span class="hl-identifier">os</span><span class="hl-default">.</span><span class="hl-identifier">path</span><span class="hl-default">.</span><span class="hl-identifier">split</span><span class="hl-brackets">(</span><span class="hl-identifier">open_file</span><span class="hl-brackets">)
			</span><span class="hl-reserved">else</span><span class="hl-default">:
				</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Error opening file</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
		</span><span class="hl-reserved">except</span><span class="hl-default">:
			</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">show_error_dlg</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Error opening file</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span></pre></div></div>
<p>You&#8217;ll notice that when loading items from the list we use a counter (count) and the has_key() function.  As explained above we save each Wine object using the gtk.TreeIter path, which is a single number since we are using a gtk.ListStore.  But since the order in the file cannot be guaranteed we use our own counter to get each item from the file in order starting at zero and working our way up until the key represented by our number is no longer in the file.  (<strong>Note:</strong> we convert out integer to a string since the keys in the shelve files must be strings.)</p>
<p>To load a Wine object from the file we simply ask the shelve file for the item at the current count key:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre><span class="hl-identifier">newwine</span><span class="hl-default"> = </span><span class="hl-identifier">db</span><span class="hl-brackets">[</span><span class="hl-builtin">str</span><span class="hl-brackets">(</span><span class="hl-identifier">count</span><span class="hl-brackets">)]</span></pre></div></div>
<p>Then we just append that wine to the list, and we have loaded a .pwi file.  The Try except code, basically just catches any error that might occur if the user tries to open a file that is not a true pyWine project file.</p>
<h2>Conclusion</h2>
<p>That&#8217;s basically it for this tutorial, but if you&#8217;ve understood it you can see how easy to would be to hook in the File | New menu handler or add a Delete button to the toolbar, or even set the title of the Window to be the current project file.</p>
<p>You might even want to try playing around with different project file types and try implementing an XML file type.  In the future I think it would be a neat option to allow the user to decide which type of file they want to use for their project files.</p>
<p>You can download the complete code for this tutorial <a href="http://www.learningpython.com/sources/PyWine_02.tar.gz">here</a>.</p>
<p>If you have any questions, or notice any problems with this tutorial please post a comment and let me know!</p>
<div style="float:right;margin:0px 0px 0px 0px;"><a href="http://www.google.com/reader/link?url=http://www.learningpython.com/2006/09/02/extending-our-pygtk-application/&title=Extending our PyGTK Application&srcTitle=learning python&srcURL=http://www.learningpython.com"target="_blank" rel=""><img border="0" src="http://www.learningpython.com/wp-content/plugins/wp-google-buzz/icon/12.png" style="opacity:1;filter:alpha(opacity=100)" onmouseover="this.style.opacity=0.8;this.filters.alpha.opacity=70" onmouseout="this.style.opacity=1;this.filters.alpha.opacity=100"/> </a></div>]]></content:encoded>
			<wfw:commentRss>http://www.learningpython.com/2006/09/02/extending-our-pygtk-application/feed/</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
		<item>
		<title>WordPy offline blogging tool</title>
		<link>http://www.learningpython.com/2006/08/19/wordpress-python-library/</link>
		<comments>http://www.learningpython.com/2006/08/19/wordpress-python-library/#comments</comments>
		<pubDate>Sat, 19 Aug 2006 17:31:20 +0000</pubDate>
		<dc:creator>selsine</dc:creator>
				<category><![CDATA[PyGTK]]></category>
		<category><![CDATA[WordPy]]></category>
		<category><![CDATA[glade]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://www.learningpython.com/?p=30</guid>
		<description><![CDATA[
			
				
			
		
Topics covered in this tutorial:

Glade
PyGTK
gtk.FileChooserDialog
gtk.FileFilter
gtk.TextView
gtk.TextBuffer
gtk.TextMark
gtk.MessageDialog

You can download the full source for this tutorial here.
One of the things that I found the other day while I was surfing the web looking for information on Python was this WordPress Python library.  Since I use Wordpress for this site I thought that I&#8217;d play with it a [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.learningpython.com%2F2006%2F08%2F19%2Fwordpress-python-library%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.learningpython.com%2F2006%2F08%2F19%2Fwordpress-python-library%2F&amp;style=normal" height="61" width="50" /><br />
			</a>
		</div>
<p>Topics covered in this tutorial:</p>
<ul>
<li>Glade</li>
<li>PyGTK</li>
<li>gtk.FileChooserDialog</li>
<li>gtk.FileFilter</li>
<li>gtk.TextView</li>
<li>gtk.TextBuffer</li>
<li>gtk.TextMark</li>
<li>gtk.MessageDialog</li>
</ul>
<p>You can download the full source for this tutorial <a href="http://www.learningpython.com/sources/WordPy_0_1.tar.gz">here</a>.</p>
<p>One of the things that I found the other day while I was surfing the web looking for information on Python was this <a href="http://www.blackbirdblog.it/programmazione/progetti/28#english">WordPress Python library</a>.  Since I use Wordpress for this site I thought that I&#8217;d play with it a bit.  The result is this tutorial about using the Wordpress library, PyGTK, and Glade.</p>
<p>The first think to do is download the <a href="http://www.blackbirdblog.it/programmazione/progetti/28">library</a>, extract it form the archive, and install it.  Enter the following on the command line in the directory you downloaded the library to in order to install it:</p>
<p><code>python setup.py install</code></p>
<p>Now you have the library installed.  The next step is to create the GUI that we will use to interact with the library.  The GUI will be created using <a href="http://glade.gnome.org/">Glade</a>, if you are new to Glade or PyGTK you might want to read over my two turorials on the subject:  <a href="http://www.learningpython.com/2006/05/07/creating-a-gui-using-pygtk-and-glade/">Creating a GUI using PyGTK and Glade</a> and <a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">Building an Application with PyGTK and Glade</a>.</p>
<p><span id="more-30"></span></p>
<h2>The GUI</h2>
<p>The first thing that we want to do is create the main GUI, here are the steps for that:</p>
<p><b>Note:</b> These steps are very general if you have trouble following them post a question or read the two initial PyGTK/Glade tutorials in order to become familiar with Glade.</p>
<ol>
<li>Add a window to the project. Set it&#8217;s name to be: &#8216;&#8221;wndMain&#8221; and its title to be: &#8220;WordPy&#8221;.  Set it&#8217;s default height to be 350 and it&#8217;s default width to be 400.</li>
<li>Add a vertical box with five rows</li>
<li>In the first row add horizontal box with two columns, set it&#8217;s &#8220;Expand&#8221; option on the Packing tab to No.  Then add a label that says &#8220;Title:&#8221; in the first column set it&#8217;s width to be 44, and in the second column add a Text Entry.  Call the text entry: &#8220;enTitle&#8221;.  Set the Text Entries padding to be 3.</li>
<li>In the second rod add a horizontal box with 13 items.  make the first item A label, make it&#8217;s &#8220;Text:&#8221;, make it&#8217;s width 44, and make it&#8217;s X Align 0.00.  Then make the next twelve items Buttons.  The buttons will be used to mimic the buttons available when writing a wordpress post:<br />
<blockquote><p>(b)(i)(link)(b-quote)(del)(ins)(img)(ul)(ol)(li)(code)(more)</p></blockquote>
<p>Name each button using the following pattern: btnBold, btnItalic, etc, and add a clicked handler to each button.</li>
<li>In the forth row add a Text View.  On the Widget tab set the Text View&#8217;s (not the Scrolled Window) name to be &#8220;txtPost&#8221; and its wrapping option to be &#8220;Word&#8221;.</li>
<li>Now add another Horizontal Button box to the final row.  Set it&#8217;s layout to be &#8220;End&#8221; and its spacing to be 3.   On the Packing tab set the padding to be 2 and Expand to No.  Set the left-most buttons name to be btnSettings, its label to be &#8220;Settings&#8221;, and it&#8217;s icon to be the &#8220;Properties&#8221; icon.  Set the other buttons name to be btnPost, it&#8217;s label to be &#8220;Post&#8221; and it&#8217;s icon to be the Up icon.</li>
<li>Add a handler for the &#8220;clicked&#8221; signal for each button.  We will use this signal to perform actions when the user clicks on the button.</li>
<li>Add a handler for the wndMain destroy signal (not the destroy_event signal, the destroy signal)</li>
</ol>
<p>That&#8217;s it for the main window, once you have completed all of those steps you should be left with something like this:</p>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/WordPy_01/wordpy_01.png" alt="GLADE Window" border="0" /></p>
<p>Note that I used icons for my buttons instead of using text.  The icons are taken from the latest version of the gnome-icon-theme.</p>
<p>The next GUI element that we are going to create is the Settings dialog that will come up when you press the settings button:</p>
<ol>
<li>Create a new dialog by pressing the dialog button.  Select the Standard Button Layout with Cancel and Ok as your buttons.  Set the dialogs name to be &#8220;dlgSettings&#8221; and its title to be &#8220;Settings&#8221;</li>
<li>Add a new table with three rows and two columns.</li>
<li>In the first row add a Label in the first column and set its label to be &#8220;URL:&#8221;.  In the second column add a Text Entry and set it&#8217;s name to be &#8220;enURL&#8221;.  On the packing tab set the Text Entries H Padding and V padding to both be 2</li>
<li>Replicate step three in row two, except set the Label&#8217;s label to be &#8220;Username&#8221; and the name of the Text Entry to be &#8220;enUsername&#8221;.</li>
<li>Replicate step three in row three, except set the Label&#8217;s label to be &#8220;Password&#8221; and the name of the Text Entry to be &#8220;enPassword&#8221;.  Set the Text Visible option to No, this will display the invisible char instead of the actual text, making this function like a normal password entry.</li>
</ol>
<p>After following those steps you should be left with something that resembles the following:</p>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/WordPy_01/wordpy_02.png" alt="GLADE Window" border="0" /></p>
<p>Finally we need to create the dialog that will be used when the user wants to insert a link into their post:</p>
<ol>
<li>Add another dialog with Ok and Cancel buttons.  Set the dialogs name to be dlgLink and it&#8217;s title to be &#8220;Insert Link&#8221;.</li>
<li>Add a vertical box with two rows, in the first row add a Label, set it&#8217;s label to be: &#8220;Enter the URL&#8221; and it&#8217;s X Allign to be 0.00.  In the second row add an Text Entry and set it&#8217;s name to be enURL.</li>
<li>On the Widget tab of dlgLink dialog&#8217;s properties set the resizable property to No, and on the Common tab set the width to be 300.</li>
</ol>
<p>your dlgLink should look something like this:</p>
<p><img style="margin: 0pt 10px 10px 0pt;" src="http://www.learningpython.com/images/WordPy_01/wordpy_04.png" alt="GLADE Window" border="0" /></p>
<h2>The Code &#8211; Connecting Glade with PyGTK</h2>
<p>Here is the basic code that we use to start up our little WordPy Application and connect it with out glade file (it is the basic code that I used in my <a href="http://www.learningpython.com/2006/05/07/creating-a-gui-using-pygtk-and-glade/">two</a> <a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/#more-25">other</a> tutorials):</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre><span class="hl-comment">#!/usr/bin/env python

</span><span class="hl-reserved">import </span><span class="hl-identifier">sys
</span><span class="hl-reserved">try</span><span class="hl-default">:
 	</span><span class="hl-reserved">import </span><span class="hl-identifier">pygtk
  	pygtk</span><span class="hl-default">.</span><span class="hl-identifier">require</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">2.0</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)
</span><span class="hl-reserved">except</span><span class="hl-default">:
  	</span><span class="hl-reserved">pass
try</span><span class="hl-default">:
	</span><span class="hl-reserved">import </span><span class="hl-identifier">gtk
  	</span><span class="hl-reserved">import </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">glade
</span><span class="hl-reserved">except</span><span class="hl-default">:
	</span><span class="hl-identifier">sys</span><span class="hl-default">.</span><span class="hl-identifier">exit</span><span class="hl-brackets">(</span><span class="hl-number">1</span><span class="hl-brackets">)

</span><span class="hl-reserved">class </span><span class="hl-identifier">WordPy</span><span class="hl-default">:
	</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">This is the Wordpy application.  It is a simple PyGTK
	application that interacts with the WorPress Python library.</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">__init__</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-brackets">)</span><span class="hl-default">:
		
		</span><span class="hl-comment">#Set the Glade file
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">gladefile</span><span class="hl-default"> = </span><span class="hl-quotes">&quot;</span><span class="hl-string">wordpy.glade</span><span class="hl-quotes">&quot;  
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default"> = </span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">glade</span><span class="hl-default">.</span><span class="hl-identifier">XML</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">.</span><span class="hl-identifier">gladefile</span><span class="hl-code">, </span><span class="hl-quotes">&quot;</span><span class="hl-string">wndMain</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">) 

		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Create our dictionary and connect it, you may notice
		that I have gone with the default function names this time</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">dic</span><span class="hl-default"> = {</span><span class="hl-quotes">&quot;</span><span class="hl-string">on_wndMain_destroy</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">quit</span><span class="hl-default">
				, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnBold_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnBold_clicked</span><span class="hl-default">
				, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnItalic_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnItalic_clicked</span><span class="hl-default">
				, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnLink_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnLink_clicked</span><span class="hl-default">
				, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnBlockQuote_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnBlockQuote_clicked</span><span class="hl-default">
				, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnDel_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnDel_clicked</span><span class="hl-default">
				, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnIns_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnIns_clicked</span><span class="hl-default">
				, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnImage_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnImage_clicked</span><span class="hl-default">
				, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnUnorderedList_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnUnorderedList_clicked</span><span class="hl-default">
				, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnOrderedList_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnOrderedList_clicked</span><span class="hl-default">
				, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnListItem_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnListItem_clicked</span><span class="hl-default">
				, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnCode_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnCode_clicked</span><span class="hl-default">
				, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnMore_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnMore_clicked</span><span class="hl-default">
				, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnSettings_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnSettings_clicked</span><span class="hl-default">
				, </span><span class="hl-quotes">&quot;</span><span class="hl-string">on_btnpost_clicked</span><span class="hl-quotes">&quot;</span><span class="hl-default"> : </span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">on_btnpost_clicked</span><span class="hl-default">}
		</span><span class="hl-identifier">self</span><span class="hl-default">.</span><span class="hl-identifier">wTree</span><span class="hl-default">.</span><span class="hl-identifier">signal_autoconnect</span><span class="hl-brackets">(</span><span class="hl-identifier">dic</span><span class="hl-brackets">)

       </span><span class="hl-reserved">def </span><span class="hl-identifier">quit</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Quit yourself</span><span class="hl-quotes">&quot;&quot;&quot;
		</span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">main_quit</span><span class="hl-brackets">()
		
		</span><span class="hl-reserved">def </span><span class="hl-identifier">on_btnBold_clicked</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the bold button is clicked</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">on_btnItalic_clicked</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the italic button is clicked</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">on_btnLink_clicked</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the link button is clicked</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">on_btnBlockQuote_clicked</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the Block Quote button is clicked</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">on_btnDel_clicked</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the Del button is clicked</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">on_btnIns_clicked</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the Ins button is clicked</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">on_btnImage_clicked</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the Image button is clicked</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">on_btnUnorderedList_clicked</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the Unordered List button is clicked</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">on_btnOrderedList_clicked</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the Ordered List button is clicked</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">on_btnListItem_clicked</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the List Item button is clicked</span><span class="hl-quotes">&quot;&quot;&quot;

	</span><span class="hl-reserved">def </span><span class="hl-identifier">on_btnMore_clicked</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the bold button is clicked</span><span class="hl-quotes">&quot;&quot;&quot;
		
	</span><span class="hl-reserved">def </span><span class="hl-identifier">on_btnCode_clicked</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the Code button is clicked</span><span class="hl-quotes">&quot;&quot;&quot;
		
	</span><span class="hl-reserved">def </span><span class="hl-identifier">on_btnSettings_clicked</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the settings button is clicked.  It will
		show the dlgSettings dialog and let the user set the WordPress
		blog settings.</span><span class="hl-quotes">&quot;&quot;&quot;
		
	</span><span class="hl-reserved">def </span><span class="hl-identifier">on_btnpost_clicked</span><span class="hl-brackets">(</span><span class="hl-identifier">self</span><span class="hl-code">, </span><span class="hl-identifier">widget</span><span class="hl-brackets">)</span><span class="hl-default">:
		</span><span class="hl-quotes">&quot;&quot;&quot;</span><span class="hl-string">Called when the post button is clicked, this will
		post the post to the blog.</span><span class="hl-quotes">&quot;&quot;&quot;

</span><span class="hl-reserved">if </span><span class="hl-identifier">__name__</span><span class="hl-default"> == </span><span class="hl-quotes">&quot;</span><span class="hl-string">__main__</span><span class="hl-quotes">&quot;</span><span class="hl-default">:
	</span><span class="hl-identifier">word</span><span class="hl-default"> = </span><span class="hl-identifier">WordPy</span><span class="hl-brackets">()
	</span><span class="hl-identifier">gtk</span><span class="hl-default">.</span><span class="hl-identifier">main</span><span class="hl-brackets">()</span></pre></div></div>
<p>This code simply shows our wndMain and connects all of our signals with internal functions.  The