# Main OpenEV Files

The following is not intended to be a high-level description of OpenEV's classes, as a complete description can be found in the Library Design Document. This is meant to describe the purpose and a bit about six key openev files. These should provide a suitable starting point for learning and experimentation.

## openev

This is a shell script that can be used to launch openev. All it does is set some environment variables, then run openev.py. It varies depending on the type of installation.

Here are some examples:

Type 1:

    #!/bin/sh
    OPENEV_HOME=`dirname $0`/..

    # Setup Environment Variables for OpenEV

    PYTHONHOME=${OPENEV_HOME}
    PYTHONPATH=${OPENEV_HOME}/pymod:${PYTHONPATH}
    GEOTIFF_CSV=${OPENEV_HOME}/share/gdal
    LD_LIBRARY_PATH=${OPENEV_HOME}/lib:${LD_LIBRARY_PATH}
    PATH=${OPENEV_HOME}/bin:${PATH}

    # check for accelerated hardware flag
    if [ "${1}" = "-h" ]; then
        shift
        echo "Setup for user installed hardware acceleration"
    else
        echo "Default software rendering mode (use -h if accelerated video card installed)."
        LD_LIBRARY_PATH=${OPENEV_HOME}/lib/Mesa:${LD_LIBRARY_PATH}
    fi

    export PYTHONHOME PYTHONPATH LD_LIBRARY_PATH PATH OPENEV_HOME GEOTIFF_CSV

    # Run OpenEV
    ${OPENEV_HOME}/pymod/openev.py "$@"

This one will run without requiring a setup script to be sourced, because it sets all the environment variables it needs. This is the type of script included in Frank Warmerdam's ftp site distributions.

Type 2:

    #!/bin/sh
    ${PYTHONHOME}/bin/python ${OPENEVHOME}/pymod/openev.py $*

This type assumes that a setup script (eg. .cshrc) has set all the necessary variables, and simply launches OpenEV.

## openev.py

This is a python script that creates an OpenEV application instance and launches a view.

    import gviewapp
    import gview
    import gtk
    import sys
    import os
    import getopt

    if __name__ == '__main__':

        # get command line options and args
        # openev -m menufile -i iconfile -t toolfile image1 image2 ......
        (options, ifiles) = getopt.getopt(sys.argv[1:], 'm:i:t:p:')

        if os.path.isdir(os.path.join(gview.home_dir, 'config')):
            mfile = os.path.join(gview.home_dir, 'config', 'DefaultMenuFile.xml')
            ifile = os.path.join(gview.home_dir, 'config', 'DefaultIconFile.xml')
            pfile = os.path.join(gview.home_dir, 'config', 'DefaultPyshellFile.xml')
        else:
            mfile=None
            ifile=None
            pfile=None

        tfile = None

        for opt in options[0:]:
            if opt[0] == '-m':
                mfile=opt[1]
            elif opt[0] == '-i':
                ifile=opt[1]
            elif opt[0] == '-p':
                pfile=opt[1]
            elif opt[0] == '-t':
                tfile=opt[1]

        app = gviewapp.GViewApp(toolfile=tfile,menufile=mfile,iconfile=ifile,pyshellfile=pfile)
        gview.app = app      # handle to the application for other modules to use
        app.subscribe('quit',gtk.mainquit)   # connect to gtk's quit mechanism
        app.show_layerdlg()                  # show layer dialog
        app.new_view(None)                   # create initial view window
        app.do_auto_imports()  # imports modules specified in preferences

        for item in ifiles:
            app.file_open_by_name(item)     # open command line files

        gtk.mainloop()         # start the main event loop

## gview.py

This provides access to the c-level openev code. Layers, view areas, rasters, etc. are defined here, however no GUI's are defined gview.py- this is left to other modules. As an example, here is the snippet of gview.py code that creates the point of interest tool:

    class GvPoiTool(GvTool):
        """Point of Interest Selection Tool

        Signals:

        poi-changed -- generated when the POI has been changed.
        """

        get_type = _gv.gv_poi_tool_get_type
        def __init__(self, _obj=None):
            if _obj: self._o = _obj; return
            self._o = _gv.gv_poi_tool_new()

        def get_point(self):
            """ Returns the current POI """
            return _gv.gv_poi_tool_get_point(self._o)

        def set_point(self, point):
            """ Sets the current POI.

            point -- a tuple (column, row)
            """
            return _gv.gv_poi_tool_new_point(self._o, point)

It inherits from the GvTool class:

    class GvTool(_gtk.GtkObject):
        get_type = _gv.gv_tool_get_type
        def __init__(self, _obj=None):
            if _obj: self._o = _obj; return

        def activate(self, view):
            _gv.gv_tool_activate(self._o, view._o)

        def deactivate(self, view):
            _gv.gv_tool_deactivate(self._o, view._o)

        def get_view(self):
            v_o = _gv.gv_tool_get_view(self._o)
            if v_o is None:
                return None
            else:
                return GvViewArea(_obj=v_o)

        def set_boundary(self, boundary):
            """Set constraint rectangle.

            boundary -- boundary is a tuple in the form (column,row,width,height)
            """
            return _gv.gv_tool_set_boundary(self._o, boundary)

You can connect to the POI tool's poi-changed signal using the connect function:

    class my_object:
        def __init__(self):
            # The poi tool is stored in the main application's toolbar:
            # Get a handle to the main application through gview (gview.app
            # is set to the main application in openev.py).  This makes
            # use of the fact that every module is only loaded once in
            # an application, unless the "reload" command is called
            # on the module.  All subsequent imports point to the same
            # space in memory.  This means that setting gview.app in
            # openev.py results in app being accessible wherever gview is
            # loaded.
            # The application is sometimes passed as an argument
            # to make it less confusing in OpenEV's tools, but the
            # end effect is the same.

            import gview
            gview.app.toolbar.poi_tool.connect('poi-changed',self.my_update_cb)

        def my_update_cb(self,*args)
            poi_info = self.app.toolbar.get_poi()
            txt=str(poi_info[0])+'     '+str(poi_info[1])

This connects the poi tool's "poi-changed" signal to your object's "my_update_cb" callback. Note that the poi tool's "connect" function is inherited from gtk.GtkObject through the GvTool base class.

## gviewapp.py and gvviewwindow.py

These modules provide gui's to go with the gview.py objects, and super-structures to control their interaction.

The top-level application is the GViewApp class in gviewapp.py. It does not have its own gui, but initializes the layer dialog, view manager, selection manager, toolbar, tools, and preferences. The first view window is launched through a call to the GViewApp's "new_view" function, which creates a new GvViewWindow and adds tool menu entries to it. gviewapp.py also contains the code for the edit toolbar and preference dialog GUIs.

gvviewwindow.py contains the code to create an OpenEV view. This consists of a GtkWindow with an embedded menubar, icon bar, and GvViewArea (the black area- see gview.py for the wrappers, gvviewarea.c for the nitty-gritty).

Other python files that provide GUIs include layerdlg.py (the layer dialog), oeattedit.py (the vector attributes dialog launched from the edit menu), gvvectorpropdlg.py (the vector properties dialog launched by right clicking on a vector file in the layer dialog), and gvrasterpropdlg.py (the raster properties dialog).

## gvsignaler.py

One other class that you should be aware of as an OpenEV developer is the Signaler class (code below):

    """
    MODULE
       gvsignaler

    DESCRIPTION
       Provides an event subscription/notification mechanism a bit like
       Gtk signal handling.  Classes which derive from Signaler can
       publish a list of named signals.  These signals can then be
       attached to arbitrary callback methods/functions using subscribe().
       More than one callback function per signal can be attached.
       The Signaler executes the callback functions with the notify()
       method.

       Arguments to the callback functions are (in order):
         1. The Signaler instance.
         2. Any signal specific arguments provided to notify().
         3. Subscriber baggage arguments provided to subscribe().
       The baggage arguments act like the 'data' argument of Gtk signals.
    """

    class UnpublishedSignalError(Exception): pass
    class SignalExistsError(Exception): pass

    class Signaler:
        "Base class for objects with published signals"
        signal = {}  # Prevents AttributeErrors

        def publish(self, *sigs):
            "Publish one or more named signals"
            if not self.__dict__.has_key('signal'):
                self.signal = {}
            for s in sigs:
                if self.signal.has_key(s):
                    raise SignalExistsError
                self.signal[s] = [0, []]  # Blocked flag, handlers list

        def subscribe(self, name, meth, *args):
            "Attach a callback function/method to a signal"
            try:
                self.signal[name][1].append((meth, args))
            except KeyError:
                raise UnpublishedSignalError

        def unsubscribe(self, name, meth):
            "Remove a callback function/method for a named signal"
            try:
                l = len(self.signal[name][1])
                for si in range(l):
                    if self.signal[name][1][si][0] == meth:
                        del self.signal[name][1][si]
                        break
            except KeyError:
                raise UnpublishedSignalError

        def notify(self, name, *args):
            "Execute callbacks attached to the named signal"
            try:
                sig = self.signal[name]
            except KeyError:
                raise UnpublishedSignalError
            # Check for blocked signal

            if sig[0] == 0:
                for s in sig[1]:
                    apply(s[0], (self,) + args + s[1])

        def block(self, name):
            "Prevent a signal from being emitted"
            try:
                self.signal[name][0] = 1
            except KeyError:
                raise UnpublishedSignalError

        def unblock(self, name):
            "Allows a blocked signal to be emitted"
            try:
                self.signal[name][0] = 0
            except KeyError:
                raise UnpublishedSignalError

An object of this class stores a dictionary of signals that it will recognize. A new signal is added to the dictionary using the "publish" function, and the signal is emitted using the "notify" function. Another object can connect to this signal using the "subscribe" function, which appends the desired callback (passed through the subscribe arguments) to the list of callbacks for that signal. "notify" cycles through all attached callbacks for a given signal, applying them. The block/unblock functions are used to temporarily prevent the callbacks from being executed. This is similar to the gtk signaling mechanism, with the difference that the gtk signals are defined at the c-level, and are emitted and connected to through python wrappers. The gtk mechanism uses the terms "connect" and "emit" rather than "subscribe" and "notify". Some objects inherit both mechanisms, for example the Layer Dialog class:

    class LayerDlg(GtkWindow,gvsignaler.Signaler):
        def __init__(self):
            GtkWindow.__init__(self)
            self.set_title('Layers')
            self.set_usize(250, 500)
            self.set_border_width(3)
            self.set_policy(TRUE,TRUE,FALSE)
            self.connect('delete-event',self.close)
            shell = GtkVBox(spacing=3)
            self.add(shell)

            # Bunch of other code...

            # Layer list
            layerbox = GtkScrolledWindow()
            shell.pack_start(layerbox)
            if self.thumbnail:
                layerlist = GtkCList(cols=3)
            else:
                layerlist = GtkCList(cols=2)

            layerbox.add_with_viewport(layerlist)
            layerlist.set_shadow_type(SHADOW_NONE)
            layerlist.set_selection_mode(SELECTION_SINGLE)
            layerlist.set_row_height(THUMB_H + 4)
            layerlist.set_column_width(0, EYE_W)
            if self.thumbnail:
                layerlist.set_column_width(1, THUMB_W + 4)
            layerlist.connect('select-row', self.layer_selected)
            layerlist.connect('button-press-event', self.list_clicked)

            # Bunch of other code...

            # Publish signals
            self.publish('active-view-changed')

In this case, 'delete-event' is a gtk signal (the little x on the layer dialog box being clicked), as are 'select-row' and 'button-press-event', but 'active-view-changed' is a Signaler signal.

As a historical note, the GViewApp class used to be in openev.py, then was separated out to its own gviewapp.py file.

Next
Developer Course Outline
OpenEV Help