4702l3

Listing 3. GladeProjectSignals.py

#!/usr/bin/env python
"""GladeProjectSignals.py
This module helps identify signal handlers referenced in a Glade project file.
This module requires Python 2.0, compiled with the Expat extension."""

from xml.dom import minidom

def nodeIsElement(node, elName):
    """Find out whether or not a node is an element of the
    specified name."""
    return ((node.nodeType == minidom.Node.ELEMENT_NODE) and
            (node.tagName == elName))

class WidgetTreeSignals:
    """This class represents a set of signal handlers defined in a
    Glade widget hierarchy."""
    def __init__(self, domNode):
        """Initialize a new instance.
        `domNode' is a DOM node which describes a widget hierarchy."""

        self.domNode = domNode
        self.name = ""
        self.handlers = []

        self._findName()
        self._findHandlers()

    def _findName(self):
        """Find the name of the topmost widget defined by self's domNode."""

        numNames = 0
        for child in self.domNode.childNodes:
            if nodeIsElement(child, "name"):
                self.name = self._nameValue(child)
                numNames = numNames + 1
        assert(numNames == 1), "Widget has %s names." % numNames

    def _findHandlers(self):
        """Find the widget handlers specified in self's DOM subtree."""
        self._walk(self.domNode)

    def _walk(self, startNode):
        """Walk through a DOM tree.  Whenever a 'signal' node
        is identified, record its handler name."""

        if nodeIsElement(startNode, "signal"):
            self._processSignal(startNode)
        else:
            for child in startNode.childNodes:
                self._walk(child)

    def _processSignal(self, node):
        """Process a signal description node."""
        for child in node.childNodes:
            if nodeIsElement(child, "handler"):
                self._processHandler(child)

    def _processHandler(self, node):
        """Process a signal handler node."""
        self.handlers.append(self._nameValue(node))

    def _isText(self, node):
        """Find out whether or not a node is a text node."""
        return (node.nodeType == minidom.Node.TEXT_NODE)

    def _nameValue(self, node):
        """Get the value of a `name' node."""

        numNames = 0
        result = ""
        for child in node.childNodes:
            if self._isText(child):
                result = child.data
                numNames = numNames + 1
        assert (numNames == 1), "Name node has %s values." % numNames
        return result
        
class GladeProjectSignals:
    """This class extracts widget signal-handler info from a Glade
    design file.

    Given a Glade project filename, each instance of this class defines
    a dictionary, `widgets', of the toplevel widgets in the project file.

    Each dictionary key is the name of a toplevel widget or dialog.
    Each dictionary value is a WidgetTreeSignals instance"""
    def __init__(self, filename):
        """Initialize a new instance."""

        self.pathname = filename
        self.doc = minidom.parse(self.pathname)
        self.widgets = {}
        self._extractWidgets()

    def _extractWidgets(self):
        """Extract toplevel widget descriptions from a Glade design file."""

        design = self.doc.childNodes[0]
        for node in design.childNodes:
            if nodeIsElement(node, "widget"):
                widgetInfo = WidgetTreeSignals(node)
                self.widgets[widgetInfo.name] = widgetInfo

def main():
    """Module mainline (for standalone execution)"""
    project = GladeProjectSignals("test.glade")
    names = project.widgets.keys()
    names.sort()
    for widget in [project.widgets[name] for name in names]:
        print "Widget tree", widget.name
        for handler in widget.handlers:
            print "    %s" % handler
        print
        
if __name__ == "__main__":
    main()
