<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="/resources/xslt/tutorial.xsl"?>
<t:tutorial xmlns:t="http://patrick.wagstrom.net/xml/tutorial/1">
  <t:title>Creating a GNOME Web Browser with Python</t:title>
  <t:author>Patrick Wagstrom</t:author>
  <t:date>August 18th, 2004</t:date>
  <t:section title="Introduction">
    <p>
      This tutorial will show you how to build a simple web browser for the GNOME
      desktop using the <a href="http://www.python.org/">Python</a> programming
      language.  It is based off the tutorial that Erik Dasque has written for
      <a href="http://primates.ximian.com/~edasque/projects/Tutorial/glade2.html">Mono
      and C#</a>.
    </p>
    <p>
      For this tutorial you're going to need the following tools.
      <ul>
        <li><a href="http://www.python.org/">Python</a> version 2.2 or above</li>
        <li><a href="http://www.pygtk.org/">PyGTK</a>- GTK+ bindings for Python</li>
        <li><a href="http://www.mozilla.org/">Mozilla</a> - you'll need to make sure the development libraries are also installed on your system.</li>
        <li><a href="http://sourceforge.net/projects/pygtkmoz">PyGtkMoz</a> - Python bindings for the GTK+ Mozilla Widget</li>
        <li><a href="http://glade.gnome.org/">Glade</a> - GTK+ user interface builder</li>
      </ul>
      Unfortunately, installation of these tools is beyond the scope of this tutorial.  For most versions of Linux you can install them using apt-get, yum, or urpmi.
    </p>
  </t:section>
  <t:section title="Starting with Glade">
    <p>
      The first task that we'll need to do is to start with Glade.  Glade is a user interface
      builder for GTK+.  The version that we'll be using is Glade-2.  You can start it from
      the command line using the command <t:command>glade-2</t:command>.  When you start it up
      you'll see a set of windows like the ones below in figure <t:figref id="fig1"/>.
    </p>
    <t:figure id="fig1" src="pygtkmozembed-1.thumb.png" width="161" height="120" alt="Glade startup window" href="pygtkmozembed-1.png"/>
    <p>
      Click on the <t:command>new</t:command> and tell it that you want to
      start a new GNOME project.  This will make the tool palette go
      undimmed and you'll now be able to create your user interface.
      On the palette click on <t:command>Gnome</t:command> button and
      select the Gnome Application Window, as shown in figure <t:figref id="fig2"/>
    </p>
    <t:figure id="fig2" src="pygtkmozembed-2.png" width="134" height="481" alt="Select Gnome application window"/>
    <p>
      This will bring up a simple window that says <t:command>glade-2</t:command> for the title.
      Click on the toolbar in an area where there are no buttons currently.  This will select
      the toolbar and its properties will come up in the property window.  Change the size
      of the toolbar from 3 to 6.  We'll be adding three more widgets to the toolbar.
    </p>
    <t:figure id="fig3" src="pygtkmozembed-3.png" width="270" height="485" alt="Change toolbar size from 3 to 6"/>
    <p>
      The next step is to populate the toolbar with new buttons.  To
      do this we'll go back to the palette and select <t:command>GTK+
      Basic</t:command>.  We want to add two buttons in the first two
      spots and a text entry in the third spot.  When you've done
      this, your window should look like the one in figure <t:figref id="fig4"/>
    </p>
    <t:figure id="fig4" src="pygtkmozembed-4.png" width="443" height="323" alt="Add two buttons and a text entry widget"/>
    <p>
      The next step is to convert the buttons we've just created to
      stock buttons.  In this case we'll want to use the forward and
      back stock buttons.  Click on the buttons, this will bring up
      their properties in the property window.  Click on the
      <t:command>Stock Button</t:command> pull down and select Forward and
      Back for the respective buttons.
    </p>
    <t:figure id="fig5" src="pygtkmozembed-5.png" width="270" height="485" alt="Change the buttons to the stock buttons"/>
    <p>
      As right now we're focusing on keeping the browser simple, most
      of the widgets aren't going to be hooked up to anything.  But we
      do want to hook up the location bar so we can type URLs and look
      at different web pages.  To do this, click on the text entry we
      added to make it active in the properties window.  The select
      the <t:command>signals</t:command> tab.  Click on the ellipsis by
      the <t:command>signal</t:command> field and select
      <t:command>activate</t:command>.  A text entry emits the activate
      signal when the enter key is pressed in it.  This will make a
      default handler of <t:command>on_entry1_activate</t:command>, which
      is fine.  Click on the <t:command>Add</t:command> button to add it.
      If you don't click the <t:command>Add</t:command> button it won't be
      added and you're work will be for naught.
    </p>
    <t:figure id="fig7" src="pygtkmozembed-7.png" width="270" height="485" alt="Adding the signal handler for the entry"/>
    <p>
      The final step we'll want to do in glade is to add a holder for
      our Mozilla widget.  Unfortunately, Glade doesn't have the
      ability to add the widget natively, but that's not a big deal.
      We do this by adding a frame to the application window.  Select
      the frame widget from the bottom of the GTK+ Basic widgets, then
      click to add it into the open spot in the application.  The
      click on the label and hit the delete key to remove the label
      from the widget.  When you're done, you should have a window
      that looks like the one shown below in figure <t:figref
      id="fig6"/>.
    </p>
    <t:figure id="fig6" src="pygtkmozembed-6.png" width="458" height="323" alt="Our final window in Glade"/>
    <p>
      Click on the save button in Glade window that has
      <t:command>app1</t:command> listed.  Save the program as
      <t:command>pygtkmozembed</t:command>.  The rest of the options don't
      really matter because we're not actually generating the code for
      C or C++.  Just hit <t:command>OK</t:command>.  
    </p>
  </t:section>
  <t:section title="Python Code">
    <p>
      Now on to the meat of the program, the Python.  Although, you'll
      soon see that there isn't much to this, Glade has taken care of
      a lot of the work we thought we'd have to do.  To start off we
      need some initial code that will be helpful.  The code shown in
      snippet <t:snipref id="snip1" /> make it so the program can run by
      itself, imports all the libraries we need and also sets some
      constants for our program that will be useful.
    </p>
    <t:snip id="snip1" alt="Importing code">
<pre>
<span style="font-style: italic"><span style="color: #9A1900">#!/usr/bin/env python</span></span>

<span style="font-weight: bold"><span style="color: #0000FF">import</span></span> gtk
<span style="font-weight: bold"><span style="color: #0000FF">import</span></span> gnome<span style="color: #990000">.</span>ui
<span style="font-weight: bold"><span style="color: #0000FF">import</span></span> gtk<span style="color: #990000">.</span>glade
<span style="font-weight: bold"><span style="color: #0000FF">import</span></span> gtkmoz

APPNAME<span style="color: #990000">=</span><span style="color: #FF0000">"PyGtkMozEmbed"</span>
APPVERSION<span style="color: #990000">=</span><span style="color: #FF0000">"0.1"</span>
</pre>
    </t:snip>
    <p>
      The next thing we'll want to do is to create a simple wrapper
      that will initialize GNOME application and load all of the
      widgets.  We'll call this class WidgetsWrapper as it wraps
      around all of our widgets.  The code for this appears in snippet
      <t:snipref id="snip2" />.
    </p>
    <t:snip id="snip2" alt="Widgets wrapper">
<pre>
<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> WidgetsWrapper<span style="color: #990000">:</span>
    <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">__init__</span></span><span style="color: #990000">(</span>self<span style="color: #990000">)</span><span style="color: #990000">:</span>
        gnome<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">init</span></span><span style="color: #990000">(</span>APPNAME<span style="color: #990000">,</span> APPVERSION<span style="color: #990000">)</span>
        self<span style="color: #990000">.</span>widgets <span style="color: #990000">=</span> gtk<span style="color: #990000">.</span>glade<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">XML</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"pygtkmozembed.glade"</span><span style="color: #990000">)</span>
        self<span style="color: #990000">.</span>mozillaWidget <span style="color: #990000">=</span> gtkmoz<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">MozEmbed</span></span><span style="color: #990000">(</span><span style="color: #990000">)</span>
        self<span style="color: #990000">.</span>widgets<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">get_widget</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"frame1"</span><span style="color: #990000">)</span><span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">add</span></span><span style="color: #990000">(</span>self<span style="color: #990000">.</span>mozillaWidget<span style="color: #990000">)</span>
        self<span style="color: #990000">.</span>mozillaWidget<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">set_size_request</span></span><span style="color: #990000">(</span><span style="color: #993399">800</span><span style="color: #990000">,</span><span style="color: #993399">600</span><span style="color: #990000">)</span>
        self<span style="color: #990000">.</span>mozillaWidget<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">show</span></span><span style="color: #990000">(</span><span style="color: #990000">)</span>
        self<span style="color: #990000">.</span>mozillaWidget<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">load_url</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"http://www.mozilla.org/"</span><span style="color: #990000">)</span>

        signalDic <span style="color: #990000">=</span> <span style="color: #990000">{</span> <span style="color: #FF0000">"on_entry1_activate"</span> <span style="color: #990000">:</span> self<span style="color: #990000">.</span>on_entry1_activate<span style="color: #990000">,</span>
                      <span style="color: #FF0000">"on_quit1_activate"</span> <span style="color: #990000">:</span> self<span style="color: #990000">.</span>on_quit1_activate<span style="color: #990000">}</span>
        self<span style="color: #990000">.</span>widgets<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">signal_autoconnect</span></span><span style="color: #990000">(</span>signalDic<span style="color: #990000">)</span>
</pre>
    </t:snip>
    <p>
      Briefly this what the program does up to this point.  First is
      registers itself as a Gnome application.  Then it loads up all
      of the widgets from our glade file we just created.  This is
      what will allow us to handle signals that are sent by widgets.
      Then it creates a widget called <t:command>mozillaWidget</t:command>
      which will hold our instance of a Mozilla web browser.  Before
      we can use the widget, we need to add it to our program, which
      is what the line after that does.  Finally, the widget is told
      to make itself visible and then load the mozilla home page as a
      start page.
    </p>
    <p>
      We're to the point where we should have something that is
      usable.  Add the following little bit of glue code at the end of
      your program and we'll have a runnable program.  If you want to
      test it at this point, you'll be able to run it and browse the
      Mozilla.org webpage and any pages that link to it, but you can't
      use the address bar yet.
    </p>
    <t:snip id="snip3" alt="Glue code">
<pre>
<span style="font-weight: bold"><span style="color: #0000FF">if</span></span> __name__ <span style="color: #990000">=</span><span style="color: #990000">=</span> <span style="color: #FF0000">"__main__"</span><span style="color: #990000">:</span>
    widgets <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">WidgetsWrapper</span></span><span style="color: #990000">(</span><span style="color: #990000">)</span>
    gtk<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">mainloop</span></span><span style="color: #990000">(</span><span style="color: #990000">)</span>
</pre>
    </t:snip>
    <p>
      But we're not happy with that.  There are a couple of issues.
      First the program doesn't exit nicely.  You can kill the window,
      but you still need to use Ctrl-C to kill the program.  Secondly,
      the address bar doesn't work.  This is where our signals take
      hold.  To do that, we first add the code in snippet <t:snipref
      id="snip4"/> to our <t:command>WidgetsWrapper.__init__</t:command>
      and then we add the methods in snippet <t:snipref id="snip5"/> to
      our WidgetsWrapper class.
    </p>
    <t:snip id="snip4" alt="Attaching signal handler">
      <pre>
        signalDic <span style="color: #990000">=</span> <span style="color: #990000">{</span> <span style="color: #FF0000">"on_entry1_activate"</span> <span style="color: #990000">:</span> self<span style="color: #990000">.</span>on_entry1_activate<span style="color: #990000">,</span>
                      <span style="color: #FF0000">"on_quit1_activate"</span> <span style="color: #990000">:</span> self<span style="color: #990000">.</span>on_quit1_activate<span style="color: #990000">}</span>
        self<span style="color: #990000">.</span>widgets<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">signal_autoconnect</span></span><span style="color: #990000">(</span>signalDic<span style="color: #990000">)</span>
    </pre>
    </t:snip>
    <t:snip id="snip5" alt="Signal handlers">
<pre>
    <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">on_entry1_activate</span></span><span style="color: #990000">(</span>self<span style="color: #990000">,</span> widget<span style="color: #990000">)</span><span style="color: #990000">:</span>
        self<span style="color: #990000">.</span>mozillaWidget<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">load_url</span></span><span style="color: #990000">(</span>widget<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">get_text</span></span><span style="color: #990000">(</span><span style="color: #990000">)</span><span style="color: #990000">)</span>
        
    <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">on_quit1_activate</span></span><span style="color: #990000">(</span>self<span style="color: #990000">,</span> widget<span style="color: #990000">)</span><span style="color: #990000">:</span>
        gtk<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">main_quit</span></span><span style="color: #990000">(</span><span style="color: #990000">)</span>
</pre>
    </t:snip>
    <p>
      <t:command>signal_autoconnect</t:command> takes a <t:command>dict</t:command>
      and connects the signals to the appropriate functions.  In a future tutorial
      I'll show a niftier way to do this, but this will work fine for now.
    </p>
    <p>
      Then we simply create the methods.  Both of the methods just
      take one argument, the widget that emitted the signal.  For our
      text entry, we just want to load the new URL that has been
      entered.  The other method allows you to use the
      <t:command>file-&gt;quit</t:command> menu option to exit the
      program.
    </p>
    <t:snip id="snip6" alt="Full program code">
<pre>
<span style="font-style: italic"><span style="color: #9A1900">#!/usr/bin/env python</span></span>

<span style="font-weight: bold"><span style="color: #0000FF">import</span></span> gtk
<span style="font-weight: bold"><span style="color: #0000FF">import</span></span> gnome<span style="color: #990000">.</span>ui
<span style="font-weight: bold"><span style="color: #0000FF">import</span></span> gtk<span style="color: #990000">.</span>glade
<span style="font-weight: bold"><span style="color: #0000FF">import</span></span> gtkmoz

APPNAME<span style="color: #990000">=</span><span style="color: #FF0000">"PyGtkMozEmbed"</span>
APPVERSION<span style="color: #990000">=</span><span style="color: #FF0000">"0.1"</span>

<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> WidgetsWrapper<span style="color: #990000">:</span>
    <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">__init__</span></span><span style="color: #990000">(</span>self<span style="color: #990000">)</span><span style="color: #990000">:</span>
        gnome<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">init</span></span><span style="color: #990000">(</span>APPNAME<span style="color: #990000">,</span> APPVERSION<span style="color: #990000">)</span>
        self<span style="color: #990000">.</span>widgets <span style="color: #990000">=</span> gtk<span style="color: #990000">.</span>glade<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">XML</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"pygtkmozembed.glade"</span><span style="color: #990000">)</span>
        self<span style="color: #990000">.</span>mozillaWidget <span style="color: #990000">=</span> gtkmoz<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">MozEmbed</span></span><span style="color: #990000">(</span><span style="color: #990000">)</span>
        self<span style="color: #990000">.</span>widgets<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">get_widget</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"frame1"</span><span style="color: #990000">)</span><span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">add</span></span><span style="color: #990000">(</span>self<span style="color: #990000">.</span>mozillaWidget<span style="color: #990000">)</span>
        self<span style="color: #990000">.</span>mozillaWidget<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">set_size_request</span></span><span style="color: #990000">(</span><span style="color: #993399">800</span><span style="color: #990000">,</span><span style="color: #993399">600</span><span style="color: #990000">)</span>
        self<span style="color: #990000">.</span>mozillaWidget<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">show</span></span><span style="color: #990000">(</span><span style="color: #990000">)</span>
        self<span style="color: #990000">.</span>mozillaWidget<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">load_url</span></span><span style="color: #990000">(</span><span style="color: #FF0000">"http://www.mozilla.org/"</span><span style="color: #990000">)</span>

        signalDic <span style="color: #990000">=</span> <span style="color: #990000">{</span> <span style="color: #FF0000">"on_entry1_activate"</span> <span style="color: #990000">:</span> self<span style="color: #990000">.</span>on_entry1_activate<span style="color: #990000">,</span>
                      <span style="color: #FF0000">"on_quit1_activate"</span> <span style="color: #990000">:</span> self<span style="color: #990000">.</span>on_quit1_activate<span style="color: #990000">}</span>
        self<span style="color: #990000">.</span>widgets<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">signal_autoconnect</span></span><span style="color: #990000">(</span>signalDic<span style="color: #990000">)</span>
        
    <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">on_entry1_activate</span></span><span style="color: #990000">(</span>self<span style="color: #990000">,</span> widget<span style="color: #990000">)</span><span style="color: #990000">:</span>
        self<span style="color: #990000">.</span>mozillaWidget<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">load_url</span></span><span style="color: #990000">(</span>widget<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">get_text</span></span><span style="color: #990000">(</span><span style="color: #990000">)</span><span style="color: #990000">)</span>
        
    <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> <span style="font-weight: bold"><span style="color: #000000">on_quit1_activate</span></span><span style="color: #990000">(</span>self<span style="color: #990000">,</span> widget<span style="color: #990000">)</span><span style="color: #990000">:</span>
        gtk<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">main_quit</span></span><span style="color: #990000">(</span><span style="color: #990000">)</span>
        
<span style="font-weight: bold"><span style="color: #0000FF">if</span></span> __name__ <span style="color: #990000">=</span><span style="color: #990000">=</span> <span style="color: #FF0000">"__main__"</span><span style="color: #990000">:</span>
    widgets <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">WidgetsWrapper</span></span><span style="color: #990000">(</span><span style="color: #990000">)</span>
    gtk<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #000000">mainloop</span></span><span style="color: #990000">(</span><span style="color: #990000">)</span>

</pre>
</t:snip>
    <p>
      Now you'll want to try and run the program.  The simplest way is
      just to type <t:command>python pygtkmozembed.py</t:command>.
      However, there is a chance you'll get an error about not being
      able to find libraries.  This is because either you haven't
      added the Mozilla library to your
      <t:command>/etc/ld.so.conf</t:command> or it's not in your
      <t:command>LD_LIBRARY_PATH</t:command> environment variable.  In
      that case you might want to try running the program by typing
      <t:command>LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib/mozilla-1.6
      python pygtkmozembed.py</t:command>.  With the program running you should see something like the picture in figure <t:figref id="fig8"/>.
    </p>
    <t:figure id="fig8" src="pygtkmozembed-8.thumb.png" width="200" height="179" alt="Final program running" href="pygtkmozembed-8.png"/>
    <p>
      Of course, I'd be remiss if I didn't show that I can do all sorts of other langauges
      too.  This is all due to the fact that we used stock widgets in our program.  If you run the program using the command line <t:command>LANG=ar_SA python pygtkmozembed.com</t:command> the menus will be Arabic and the text will appear right to left.
    </p>
    <t:figure id="fig9" src="pygtkmozembed-9.thumb.png" width="200" height="179" alt="Browsing the web in Arabic" href="pygtkmozembed-9.png"/>
    <p>
      At this point, our program works well enough, and this covers everything
      that Erik covered in his tutorial.  In the future I'll probably add some
      updates that show how to do some more interesting stuff such as activating
      the forward and back widgets.  Also, you'll notice that there is no progress
      given on the loading of a web page, and no errors are hooked up.  Those
      will be covered in the future too.
    </p>
  </t:section>
</t:tutorial>
