///wxHTML for Beginners

wxHTML for Beginners

Building Basic Browser Functionality with wxPython

Many applications use HTML for reporting and online help, among other things. Embedding a Web browser in your application eliminates the need to worry about which browser a client uses to view your pages, and also allows you to create custom tags that tie the HTML page back to your application. For example, for the help system in an IDE, a user could browse a function reference where you have created custom tags, and when the user clicks on the function name, the function call could be inserted into the user’s code.

Typically, most applications solve most browser problems by keeping HTML simple — writing, as it were, to the lowest common denominator. Even in this case, however, problems with fonts and layout still persist, as well as the pain of testing of your application when new browsers are released and existing browsers are upgraded. The alternative — supporting only one browser — isn’t usually a user-friendly solution.

The obvious solution is to embed your own HTML-rendering widget in your application. Of course, writing such a widget from scratch is a large job, so it seems logical to turn to a pre-packaged solution.

There are a number of choices in the commercial world, as well as a few open source packages (see Resources later in this article). This article will show you how to use the wxHtml widget distributed as part of the wxWindows package, with Python as the language of choice for the bindings (C++, Perl, and other languages are supported as well).

This article assumes a basic knowledge of wxPython, although experienced Python developers with no wxPython experience should be able to hit the ground running. In this article we will create a standalone browser application, while keeping the architecture simple enough that it would be a simple task to migrate the browser functionality into an existing application.

The World’s Most Basic Browser

The first step is to assemble the minimum code necessary to support an application using the wxHtml widget. The following code implements a basic wxPython application with an wxHtml widget as the contents of the main window.

Listing 1. Bare-bones example browser code

from wxPython.wx import *

from wxPython.html import *
import os,sys

class exHtmlWindow(wxHtmlWindow):
def __init__(self, parent, id, frame):
wxHtmlWindow.__init__(self,parent,id)

class exHtmlPanel(wxPanel):
def __init__(self, parent, id, frame):
wxPanel.__init__(self,parent,-1)

self.html = exHtmlWindow(self, -1, frame)

self.box = wxBoxSizer(wxVERTICAL)
self.box.Add(self.html, 1, wxGROW)

self.SetSizer(self.box)
self.SetAutoLayout(true)

class exFrame (wxFrame):
def __init__(self, parent, ID, title):
wxFrame.__init__(self,parent,ID,title,wxDefaultPosition,wxSize(600,750))
panel = exHtmlPanel(self, -1, self)

class exApp(wxApp):
def OnInit(self):
frame = exFrame(NULL, -1, "Example Browser")
frame.Show(true)
self.SetTopWindow(frame)
return true


app = exApp(0)
app.MainLoop()

Assuming you have wxPython correctly installed, running the above code in your Python interpreter will result in a single large window with an empty white panel (the wxHtml widget). If you get any syntax errors, check for whitespace issues — especially if you cut-and-pasted the code into your interpreter or editor. If your Python interpreter complains about not being able to import wxPython, check your installation to make sure that it is set up properly.

Of course, as soon as you launch the browser, it becomes immediately evident that we’re missing something… such as a mechanism by which to load pages. For some applications, this very basic setup may actually be enough — the user doesn’t need to choose his own pages if you already know what you want to deliver. A simple change would be to pass an extra argument to exHtmlPanel, which would be the page you want to access:

Listing 2. Modifying exHtmlPanel to load page

class exHtmlPanel(wxPanel):

+ def __init__(self, parent, id, frame, file):
wxPanel.__init__(self, parent, -1)

self.html = exHtmlWindow(self, -1, frame)

self.box = wxBoxSizer(wxVERTICAL)
self.box.Add(self.html, 1, wxGROW)

self.SetSizer(self.box)
self.SetAutoLayout(true)
+ self.html.LoadPage(file)

In the interest of making something a little more standalone and browser-like, we’ll expand the ttHtmlPanel class to add some buttons to do standard browser tasks. Of course, if you’re actually planning to build a real browser application, you might want to put some more thought into GUI design and usability than we did here.

Listing 3. Modifying ttHtmlPanel to add buttons

class ttHtmlPanel(wxPanel):

def __init__(self, parent, id, frame):
wxPanel.__init__(self, parent, -1)
self.frame = frame
self.cwd = os.path.split(sys.argv[0])[0]
if not self.cwd:
self.cwd = os.getcwd

self.html = ttHtmlWindow(self, -1, self.frame)

self.box = wxBoxSizer(wxVERTICAL)
self.box.Add(self.html, 1, wxGROW)

subbox = wxBoxSizer(wxHORIZONTAL)

btn = wxButton(self, 1202, "Load File")
EVT_BUTTON(self, 1202, self.OnLoadFile)
subbox.Add(btn, 1, wxGROW | wxALL, 2)

btn = wxButton(self, 1203, "Load Page")
EVT_BUTTON(self, 1203, self.OnLoadPage)
subbox.Add(btn, 1, wxGROW | wxALL, 2)

btn = wxButton(self, 1204, "Back")
EVT_BUTTON(self, 1204, self.OnBack)
subbox.Add(btn, 1, wxGROW | wxALL, 2)

btn = wxButton(self, 1205, "Forward")
EVT_BUTTON(self, 1205, self.OnForward)
subbox.Add(btn, 1, wxGROW | wxALL, 2)

self.box.Add(subbox, 0, wxGROW)
self.SetSizer(self.box)
self.SetAutoLayout(true)

def OnLoadPage(self, event):
dlg = wxTextEntryDialog(self, 'Location:')
if dlg.ShowModal() == wxID_OK:
self.destination = dlg.GetValue()
dlg.Destroy()
self.html.LoadPage(self.destination)

def OnLoadFile(self, event):
dlg = wxFileDialog(self, wildcard = '*.htm*', style=wxOPEN)
if dlg.ShowModal():
path = dlg.GetPath()
self.html.LoadPage(path)
dlg.Destroy()

def OnBack(self, event):
if not self.html.HistoryBack():
wxMessageBox("No more items in history!")

def OnForward(self, event):
if not self.html.HistoryForward():
wxMessageBox("No more items in history!")

If you’ve previously used wxPython, or any other Python graphics toolkit, you can see that all we’ve done is add another container to the panel and put four buttons in it, with callbacks to the added methods in the exHtmlPanel class. The base wxHtml class handily manages the history for us, so OnBack and OnForward are simply calls to the base methods.

Assuming you’ve been playing along in your Python interpreter as you read this, you may have noticed that the application never returns control back to the console if you close it. This is a simple thing to fix, but maybe we should add a menu bar to provide a file menu with an exit option:

Listing 4. Modifying exFrame to add file menu with exit

class exFrame(wxFrame):

def __init__(self, parent, ID, title):
wxFrame.__init__(self, parent, ID, title, wxDefaultPosition, wxSize(600,750))
panel = exHtmlPanel (self, -1, self)

mnu_file = wxMenu()
mnu_file.Append(101, "E&xit", "Exit the browser")

menuBar = wxMenuBar()
menuBar.Append(mnu_file, "F&ile")

self.SetMenuBar(menuBar)

EVT_MENU(self, 101, self.Exit)

def Exit(self, event):
self.Close(true)

While we’re off trying to turn this into a real browser, there are a couple of finishing touches that we’re missing: most browsers have a status bar, and you may have noticed that no images are being drawn. The following modifications to exApp, exFrame, and exHtmlPanel add a status bar and all built-in image support from wxPython:

Listing 5. Adding a status bar and image support

class exApp(wxApp):

def OnInit(self):
+ wxInitAllImageHandlers()
frame = exFrame(NULL, -1, "Example Browser")
frame.Show(true)
self.SetTopWindow(frame)
return true

class exHtmlPanel(wxPanel):
def __init__(self, parent, id, frame):
wxPanel.__init__(self, parent, -1)
self.frame = frame
self.cwd = os.path.split(sys.argv[0])[0]
if not self.cwd:
self.cwd = os.getcwd

self.html = exHtmlWindow(self, -1, self.frame)
+ self.html.SetRelatedFrame(self.frame, "%s")
+ self.html.SetRelatedStatusBar(0)
...

class exFrame(wxFrame):
def __init__(self, parent, ID, title):
wxFrame.__init__(self, parent, ID, title, wxDefaultPosition, wxSize(600,750))
panel = exHtmlPanel (self, -1, self)

+ self.CreateStatusBar()
+ self.SetStatusText("Default status bar")
...

Now your basic browser should be fully functional. Advanced features of wxPython allow you to create your own tags that are handled by custom code to do any action of your choice. Having control of your own customizable embedded browser provides an endless array of possibilities for enhanced reporting and online help.

This code could easily provide the foundation for any number of apps entirely on its own, as well — there is no reason to limit yourself just to providing online help. Feel free to play around with these classes and see what interesting behavior you can whip up. :-)

Resources

• For an intro to wxPython, see ” wxPython for newbies” ( developerWorks, March 2001).

• Read ” Looking through wxWindows ” for a discussion of the wxWindows architecture, tips for dealing with multi-platform file handling, and a brief look at wxHTML ( developerWorks , February, 2001).

• In ” Porting MFC applications to Linux“, Markus Neifer uses wxWindows as a straightforward mechanism for porting Microsoft Foundation Class applications to Linux ( developerWorks , April 2002).

• Check the wxPython home page for the latest source and documentation.

• Download basic.py, the complete code for the project described in this article.

Python Essential Reference by David Beazley (New Riders Publishing, 2001; ISBN 0735710910) is the best Python language reference on the planet, and you should own a copy.

NGLayout/Gecko is the HTML parsing and rendering component of Mozilla/Netscape, and as such supports a great many more features (CSS/XML, etc.) than wxHtml, although it is more difficult to extend.

• Find more articles on Linux topics in the developerWorks Linux zone, and more articles on Web architecture topics in the developerWorks Web architecture zone.

2010-05-26T16:56:11+00:00 October 30th, 2004|HTML|0 Comments

About the Author:

Abhijit Belapurkar has a bachelor's degree in computer science from the Indian Institute of Technology (IIT), Delhi, India. He has been working in the areas of architectures and information security for distributed applications for almost 10 years, and has used the Java platform to build n-tier applications for more than five years. He is a senior technical architect in the J2EE space with Infosys Technologies Limited in Bangalore, India.

Leave A Comment