NRAO Home  >  Green Bank  |  Wiki Topic:    GB > Software > DEAPProgrammersManual (r1.1 vs. r1.8)
   Changes | Index | Contents | Search | Statistics | Go
 <<O>>  Difference Topic DEAPProgrammersManual (r1.8 - 13 May 2004 - AmyShelton)
Changed:
<
<

DEAP is intended to provide plotting functionality independent of the DEAP application. One of the design goals was to make it easy to embed DEAP in any application while retaining all the functionality of the stand-alone application. Because of this, we can use DEAP in a variety of applications, such as GfmBeta?, LogView, Astrid, and dcmon. The framework resides in DEAP's gui/framework package.

>
>

DEAP is intended to provide plotting functionality independent of the DEAP application. One of the design goals was to make it easy to embed DEAP in any application while retaining all the functionality of the stand-alone application. Because of this, we can use DEAP in a variety of applications, such as GbtFitsMonitor, LogView, Astrid, and dcmon. The framework resides in DEAP's gui/framework package.


 <<O>>  Difference Topic DEAPProgrammersManual (r1.7 - 13 May 2004 - AmyShelton)
Deleted:
<
<

TOC: No TOC in "Software.DEAPProgrammersManual"


Changed:
<
<

DEAP Application Framework

>
>

DEAP is intended to provide plotting functionality independent of the DEAP application. One of the design goals was to make it easy to embed DEAP in any application while retaining all the functionality of the stand-alone application. Because of this, we can use DEAP in a variety of applications, such as GfmBeta?, LogView, Astrid, and dcmon. The framework resides in DEAP's gui/framework package.

Changed:
<
<

DEAP is intended to provide plotting functionality independent of the DEAP application. One of the design goals was to make it easy to embed DEAP in any application while retaining all the functionality of the stand-alone application. Because of this, we can use DEAP in a variety of applications, such as GFM, LogView, Astrid, and dcmon. The framework resides in DEAP's gui/framework package.

>
>

The primary mechanism for embedding DEAP is derived from other graphical embedding frameworks and uses Panels and Frames. The intent is to re-use panel functionality in different applications and also to allow different panels to co-exist in the same application. For example, LogView hosts both a DEAPPanel and a separate selection panel. A future version will probably also include a tabular display panel.

Changed:
<
<

The primary mechanism for embedding DEAP is derived from other graphical embedding frameworks and uses Panels and Frames. The intent is to re-use panel functionality in different applications and also to allow different panels to co-exist in the same application. For example, LogView hosts both a DEAPPanel and a separate selection panel. A future version will probably also include a tabular display panel.

>
>

The Frame class provides a basic UI shell. Panels customize the Frame interface for Panel-specific functionality. This works through cycles of activation and deactivation. In the beginning, the Frame must be told to activate a panel through the ActivatePanel method. ActivatePanel allows a new panel to take control of the top-level UI by deactivating the old/existing panel and activating the new/given panel.

Changed:
<
<

The Frame class provides a basic UI shell. Panels customize the Frame interface for Panel-specific functionality. This works through cycles of activation and deactivation. In the beginning, the Frame must be told to activate a panel through the ActivatePanel? method. ActivatePanel? allows a new panel to take control of the top-level UI by deactivating the old/existing panel and activating the new/given panel.

>
>

Activating a Panel in the Frame causes the ActivateSite method of the Panel to be called. ActivateSite notifies the Panel of its containing Frame and gives the Panel an opportunity to customize the Frame in any manner necessary.

Changed:
<
<

Activating a Panel in the Frame causes the ActivateSite? method of the Panel to be called. ActivateSite? notifies the Panel of its containing Frame and gives the Panel an opportunity to customize the Frame in any manner necessary.

A corresponding Panel method - DeactivateSite? - gives the Panel an opportunity to undo any customizations, but usually you do not need to implement this method because the default clean-up behavior in the Frame is sufficient.

>
>

A corresponding Panel method - DeactivateSite - gives the Panel an opportunity to undo any customizations, but usually you do not need to implement this method because the default clean-up behavior in the Frame is sufficient.

Changed:
<
<

The available Panel methods are the accessors GetSite? and SetSite?, the events ActivateSite? and DeactivateSite?, and UpdateMenus?. There is an important distinction between ActivateSite? and UpdateMenus?. ActivateSite? customizes the Frame and is able to add or remove menu items in the Frame. This is a one-time activity. UpdateMenus? is called every time the user accesses the menus, and it is used to reflect the current state of the application. For example, the "Save" menu option may only be available when the application is "dirty" and has changed state since the last save.

>
>

The available Panel methods are the accessors GetSite and SetSite, the events ActivateSite and DeactivateSite, and UpdateMenus. There is an important distinction between ActivateSite and UpdateMenus. ActivateSite customizes the Frame and is able to add or remove menu items in the Frame. This is a one-time activity. UpdateMenus is called every time the user accesses the menus, and it is used to reflect the current state of the application. For example, the "Save" menu option may only be available when the application is "dirty" and has changed state since the last save.

Changed:
<
<

Note that we now have a containment hierarchy in addition to an inheritance hierarchy. A Panel is contained in a Frame, but it is not derived from Frame. We can, however, use the containment hierarchy in much the same way as we use the inheritance hierarchy. In general, Frame methods are available to the Panel transparently, and this is made possible by an implementation of getattr in the Panel. The significance of this is the Panel can call GetToolBar?() or GetStatusBar?() in order to make customizations, and does not have to execute any maneuvers such as GetFrame?().GetStatusBar(). The Panel does not need to be concerned about it's containing object, and we'll soon see that this is an important distinction.

>
>

Note that we now have a containment hierarchy in addition to an inheritance hierarchy. A Panel is contained in a Frame, but it is not derived from Frame. We can, however, use the containment hierarchy in much the same way as we use the inheritance hierarchy. In general, Frame methods are available to the Panel transparently, and this is made possible by an implementation of getattr in the Panel. The significance of this is the Panel can call GetToolBar() or GetStatusBar() in order to make customizations, and does not have to execute any maneuvers such as GetFrame().GetStatusBar(). The Panel does not need to be concerned about it's containing object, and we'll soon see that this is an important distinction.

Changed:
<
<

The key point is that one layer of containment is not usually very interesting. You could write that application without any help from the DEAP framework. The real strength of the framework comes out when we want to nest panels as much as necessary. So, getting away from concrete classes, Panel defines an interface (ActivateSite?, DeactivateSite?) for contained objects while Site defines an interface (ActivatePanel?, DeactivatePanel?) for containing objects. The Frame class implements the Site interface, but the Panel class implements both the Site interface and the Panel interface. This allows nesting.

>
>

The key point is that one layer of containment is not usually very interesting. You could write that application without any help from the DEAP framework. The real strength of the framework comes out when we want to nest panels as much as necessary. So, getting away from concrete classes, Panel defines an interface (ActivateSite, DeactivateSite) for contained objects while Site defines an interface (ActivatePanel, DeactivatePanel) for containing objects. The Frame class implements the Site interface, but the Panel class implements both the Site interface and the Panel interface. This allows nesting.

Changed:
<
<

In an application with nested Panels, the ActivatePanel? method propagates up through the containment hierarchy until it hits the Frame, and the ActivateSite? method propagates down through the containment hierarchy until it hits the innermost Panel. All of this is provided in the gui.framework.Panel class.

>
>

In an application with nested Panels, the ActivatePanel method propagates up through the containment hierarchy until it hits the Frame, and the ActivateSite method propagates down through the containment hierarchy until it hits the innermost Panel. All of this is provided in the gui.framework.Panel class.

Changed:
<
<

A consequence of the nesting behavior is that all nested Panels get a chance to customize the UI from the inside-out. That is to say, your derived Panel class overrides ActivateSite?. The overridden method calls its parent's ActivateSite?, and the default implementation activates the contained Panel. Then, after the contained panel is activated (the superclass method completes), the containing panel has an opportunity to further customize the UI. It's turtles all the way down.

>
>

A consequence of the nesting behavior is that all nested Panels get a chance to customize the UI from the inside-out. That is to say, your derived Panel class overrides ActivateSite. The overridden method calls its parent's ActivateSite, and the default implementation activates the contained Panel. Then, after the contained panel is activated (the superclass method completes), the containing panel has an opportunity to further customize the UI. It's turtles all the way down.

Added:
>
>


Added:
>
>

Changed:
<
<

Also, because of the inside-out order of UI customization, containers need to know something about the behavior of their children in order to prevent conflicting customizations. The best way to work around this (i.e., to not care about what the contained objects are doing) is to always do customizations relative to the IDs defined in gui.framework.Frame.

>
>

Also, because of the inside-out order of UI customization, containers need to know something about the behavior of their children in order to prevent conflicting customizations. The best way to work around this (i.e., to not care about what the contained objects are doing) is to always do customizations relative to the IDs defined in gui.framework.Frame.

Changed:
<
<

Application provides only a convenient way of accessing options. By deriving your application from gui.framework.Application and initializing the superclass with an array of potential config files, any code in the application can use wxGetApp().GetOption to read those config files.

>
>

Application provides only a convenient way of accessing options. By deriving your application from gui.framework.Application and initializing the superclass with an array of potential config files, any code in the application can use wxGetApp().GetOption to read those config files.

Changed:
<
<

And here's how you put all this together. Do all your real work in a derived Panel class. Override ActivateSite?, DeactivateSite?, and UpdateMenus? as necessary, but be sure to always call the superclass methods first when overriding them.

>
>

And here's how you put all this together. Do all your real work in a derived Panel class. Override ActivateSite, DeactivateSite, and UpdateMenus as necessary, but be sure to always call the superclass methods first when overriding them.

Changed:
<
<

Create a stub derived Frame class that knows how to instantiate your derived Panel class. If it is appropriate, use or derive a Notebook to host your Panel(s). With or without a Notebook, the most important thing you need to do is call ActivatePanel? with your first active panel in order to hook-up all the plumbing discussed above.

>
>

Create a stub derived Frame class that knows how to instantiate your derived Panel class. If it is appropriate, use or derive a Notebook to host your Panel(s). With or without a Notebook, the most important thing you need to do is call ActivatePanel with your first active panel in order to hook-up all the plumbing discussed above.


 <<O>>  Difference Topic DEAPProgrammersManual (r1.6 - 13 May 2004 - AmyShelton)
Changed:
<
<

In the works...

>
>

TOC: No TOC in "Software.DEAPProgrammersManual"

Changed:
<
<

Creating a strip chart with DEAP

I obviously dont understand all the dynamics of the Panel/Site design, but, i've got code that works:

/users/pmargani/sandbox_trunk/sparrow/gbt/app/mydeap2

You're welcomed to try and make sense of it.

If you want to see the same thing, but instantiated twice in a notebook (ala Gfm), see:

/users/pmargani/sandbox_trunk/sparrow/gbt/app/mydeap4

If you want to see the development in progress of a Grail strip chart (charts M&C values), see:

/users/pmargani/sandbox_trunk/sparrow/gbt/app/stripChart

Here's my sad attempt at explaining it:

Providing the framework for the new Deap Panel

To use the Site/Panel mechanics used by Deap, instead of using wxApps and wxFrames, you use Deap's special classes in gui.framework: Application, Frame, Panel ...

Application:

Here's what a basic entry point for our new app looks like (mydeap.py):

#! /usr/bin/env python

import os
import sys

sys.path.insert(1, os.environ["DEAP"])

from   mydeapFrame  import mydeapFrame
from   gui.framework import Application
from   wxPython.wx   import *
import os.path

CONFIG_PATH = [
    os.path.expanduser("~/.sparrow")
  , os.path.join(os.environ["SPARROW_DIR"], "sparrow.conf")
]

class mydeap(Application):
    
    def __init__(self, console, configPath = CONFIG_PATH):
        Application.__init__(self, console, configPath)
        
    def OnInit(self):
        Application.OnInit(self)
        self.frame = mydeapFrame(None, -1, "test app")
        self.frame.Show(1)
        self.SetTopWindow(self.frame)
        return 1
        
def main(console):
    app = mydeap(console)
    app.MainLoop()
    
if __name__ == '__main__':
    console    = 1
    main(console)

Frame:

The class mydeapFrame is a subclass of Frame (as mydeap is a sub-class of Application):

class mydeapFrame(Frame):
    
    def __init__(self, parent, id, title):
        Frame.__init__(self, parent, id, title, size = (800, 600))
        self.InitDEAP()
        self.InitSplitter()
        self.InitPanel()

InitDEAP?() sets up our means for using command line commands for Deap (i'll get to mydeapDocument in a minute):

    def InitDEAP(self):
        "Construct an interpreter for our shell window."
        print 'creating deap interpreter from new document'
        self.interpreter = Interpreter(mydeapDocument())

InitSplitter?() sets up splitter seperating our command line Py shell, a console window for the actual Py shell for sending command line arguments to Deap, another console window for sending standard output to, and finally our panel that will actually hold the plot.

I'll get to the mydeapPanel class in a minute, but that's where the action is.

    def InitSplitter(self):
        "Split the frame with graphical panes on top and text below."
        self.outer    = wxSplitterWindow(self, -1)
        self.notebook = self.InitNotebook(self.outer) # notebook for py shell and console window
        self.console  = self.InitConsole(self.notebook)
        self.deapPanel = mydeapPanel(self.outer, self.interpreter, self.console) # our deap implementation
        self.outer.SplitHorizontally(self.deapPanel, self.notebook, -150)
        self.outer.SetMinimumPaneSize(20)

Introducing a new widget and menu item using the Document

With the framework of the application now in place, we can implement our own Deap panel.

Our first goal is to introduce a new widget (a checkbox) and a corresoponding check menu item. To do this we create a sub-class of Panel (also from gui.framework):

Check out the comments. You can see that this Panel is the wrapper for a plotting panel, and another panel for our new widget.

class mydeapPanel(Panel):
    
    def __init__(self, parent, interpreter, console):
        Panel.__init__(self, parent)

        # for using command lines
        self.interpreter = interpreter
        self.console     = console
        self.logFile     = None

        # this is the subject in the observer pattern
        self.mydoc = mydeapDocument()
        
        # create the panel for plotting
        self.plotPanel = DEAPPanel(self,self.mydoc) 
     
        # create the panel for our new widget
        self.controlPanel = controlPanel(self,-1,self.mydoc) 
 
        # subscribe to changes made to the document
        self.mydoc.AddObserver(self.OnUpdate)

        self.ActivatePanel(self.plotPanel)

Before I get to the mydeapDocument class, I should explain that this Panel is the Panel in the Panel/Site relationship. What's the that. Each Panel can set the apperance of it's site. In this case, the site is the mydeapFrame class. In brief, this Panel will set the Frame's new menu items.

>
>


Changed:
<
<

Here's what happens:

>
>

DEAP Application Framework

Changed:
<
<

  1. in mydeapPanel.__init__, ActivatePanel? is called, using the Deap panel.
  2. This causes the ActivateSite? method in mydeapPanel to be called
  3. In ActivateSite?, the panel gets the sites View menu bar
  4. It adds a new menu item, getting this menu's item from the state stored in the Document
>
>

DEAP Application Framework

Changed:
<
<

The mydeapDocument class is sub-classed from document.Document, and is the subject in the observer pattern. That's where we store the state of our application. When changes are made to the document, all the 'observers', including this Panel, are sent updates (using Events - I'll get there).

>
>

DEAP is intended to provide plotting functionality independent of the DEAP application. One of the design goals was to make it easy to embed DEAP in any application while retaining all the functionality of the stand-alone application. Because of this, we can use DEAP in a variety of applications, such as GFM, LogView, Astrid, and dcmon. The framework resides in DEAP's gui/framework package.

Changed:
<
<

Here's the document:

>
>

The primary mechanism for embedding DEAP is derived from other graphical embedding frameworks and uses Panels and Frames. The intent is to re-use panel functionality in different applications and also to allow different panels to co-exist in the same application. For example, LogView hosts both a DEAPPanel and a separate selection panel. A future version will probably also include a tabular display panel.

Changed:
<
<

class mydeapDocument(Document):
>
>

The Frame class provides a basic UI shell. Panels customize the Frame interface for Panel-specific functionality. This works through cycles of activation and deactivation. In the beginning, the Frame must be told to activate a panel through the ActivatePanel? method. ActivatePanel? allows a new panel to take control of the top-level UI by deactivating the old/existing panel and activating the new/given panel.

Changed:
<
<

def __init__(self): Document.__init__(self) self.viewAll = 1 self.viewLast = 0

>
>

Activating a Panel in the Frame causes the ActivateSite? method of the Panel to be called. ActivateSite? notifies the Panel of its containing Frame and gives the Panel an opportunity to customize the Frame in any manner necessary.

Changed:
<
<

def SetViewAllOn?(self): self.viewAll = 1 self.viewLast = 0

>
>

A corresponding Panel method - DeactivateSite? - gives the Panel an opportunity to undo any customizations, but usually you do not need to implement this method because the default clean-up behavior in the Frame is sufficient.

Changed:
<
<

self.Update(ViewAllEvent?()) self.RePlot()

>
>

There is not much point in enumerating Frame methods because we're not interested in them. We won't be implementing any interesting derived Frame classes.

Changed:
<
<

Here's what happens when the new menu item is checked:

>
>

The available Panel methods are the accessors GetSite? and SetSite?, the events ActivateSite? and DeactivateSite?, and UpdateMenus?. There is an important distinction between ActivateSite? and UpdateMenus?. ActivateSite? customizes the Frame and is able to add or remove menu items in the Frame. This is a one-time activity. UpdateMenus? is called every time the user accesses the menus, and it is used to reflect the current state of the application. For example, the "Save" menu option may only be available when the application is "dirty" and has changed state since the last save.

Changed:
<
<

  1. In the menu item event handler we call the appropriate mydeapDocument method directly (mydeapDocument.SetViewAllOn()). We dont use 'Action' classes becasue we aren't interested in undoing/redoing.
  2. The document toggles the two state flags, and calls the Update method using teh ViewAllEvent? class.
  3. The ViewAllEvent? is a subclass of event.Event and it's only current purpose is to uniquely identify the event as a 'ViewAll'.
  4. All objects that subcribed to the document, get an update, with id 'ViewAll'. In this case, our control panel get's this event, and, from the id of the event knows to toggle it's checkbox.
>
>

Note that we now have a containment hierarchy in addition to an inheritance hierarchy. A Panel is contained in a Frame, but it is not derived from Frame. We can, however, use the containment hierarchy in much the same way as we use the inheritance hierarchy. In general, Frame methods are available to the Panel transparently, and this is made possible by an implementation of getattr in the Panel. The significance of this is the Panel can call GetToolBar?() or GetStatusBar?() in order to make customizations, and does not have to execute any maneuvers such as GetFrame?().GetStatusBar(). The Panel does not need to be concerned about it's containing object, and we'll soon see that this is an important distinction.

Changed:
<
<

Here are the code snippets:

>
>

The key point is that one layer of containment is not usually very interesting. You could write that application without any help from the DEAP framework. The real strength of the framework comes out when we want to nest panels as much as necessary. So, getting away from concrete classes, Panel defines an interface (ActivateSite?, DeactivateSite?) for contained objects while Site defines an interface (ActivatePanel?, DeactivatePanel?) for containing objects. The Frame class implements the Site interface, but the Panel class implements both the Site interface and the Panel interface. This allows nesting.

Changed:
<
<

ViewAllEvent?.py:

from event.Event import *
>
>

In an application with nested Panels, the ActivatePanel? method propagates up through the containment hierarchy until it hits the Frame, and the ActivateSite? method propagates down through the containment hierarchy until it hits the innermost Panel. All of this is provided in the gui.framework.Panel class.

Changed:
<
<

class ViewAllEvent?(Event): """ This class is a container for information contained in a "set last data" event. """

>
>

A consequence of the nesting behavior is that all nested Panels get a chance to customize the UI from the inside-out. That is to say, your derived Panel class overrides ActivateSite?. The overridden method calls its parent's ActivateSite?, and the default implementation activates the contained Panel. Then, after the contained panel is activated (the superclass method completes), the containing panel has an opportunity to further customize the UI. It's turtles all the way down.

Changed:
<
<

def __init__(self): "Initialize the event for when a title is set."

>
>

In general, you don't have to pay much attention to the Site/Panel hierarchy. Even the innermost nested Panel class can transparently call Frame methods to customize the UI. There are, however, two places where the illusion breaks down. One, in order to catch and handle events on the Frame, such as a menu selection, the Panel has to explicitly access the Frame:

Changed:
<
<

Event.__init__(self, 38)

>
>

EVT_WHATEVER(self.GetFrame(), self.OnWhatever)

Changed:
<
<

>
>

Also, because of the inside-out order of UI customization, containers need to know something about the behavior of their children in order to prevent conflicting customizations. The best way to work around this (i.e., to not care about what the contained objects are doing) is to always do customizations relative to the IDs defined in gui.framework.Frame.

Changed:
<
<

Control panel get's the update:

    def OnUpdate(self, event):
        if event.GetId() == 38: #view all set
            self.viewAll.SetValue(1)
            self.viewLast.SetValue(0)
>
>

Frame and, especially, Panel are the core parts of the framework. Two other classes are Notebook and Application. Notebook is designed to work in the context of the Site/Panel interfaces and will always tell its containing object (Site) to activate the currently selected panel.

Changed:
<
<

>
>

Application provides only a convenient way of accessing options. By deriving your application from gui.framework.Application and initializing the superclass with an array of potential config files, any code in the application can use wxGetApp().GetOption to read those config files.

Added:
>
>

And here's how you put all this together. Do all your real work in a derived Panel class. Override ActivateSite?, DeactivateSite?, and UpdateMenus? as necessary, but be sure to always call the superclass methods first when overriding them.

Added:
>
>

Create a stub derived Frame class that knows how to instantiate your derived Panel class. If it is appropriate, use or derive a Notebook to host your Panel(s). With or without a Notebook, the most important thing you need to do is call ActivatePanel? with your first active panel in order to hook-up all the plumbing discussed above.

Added:
>
>

Create a stub Application class that knows how to instantiate your derived Frame class and where to find your config file, if appropriate.

Changed:
<
<


>
>

Then you're good to go.

Deleted:
<
<

-- AmyShelton - 06 Apr 2004


 <<O>>  Difference Topic DEAPProgrammersManual (r1.5 - 08 Apr 2004 - PaulMarganian)
Added:
>
>

If you want to see the development in progress of a Grail strip chart (charts M&C values), see:

/users/pmargani/sandbox_trunk/sparrow/gbt/app/stripChart


 <<O>>  Difference Topic DEAPProgrammersManual (r1.4 - 08 Apr 2004 - PaulMarganian)
Added:
>
>

If you want to see the same thing, but instantiated twice in a notebook (ala Gfm), see:

/users/pmargani/sandbox_trunk/sparrow/gbt/app/mydeap4


 <<O>>  Difference Topic DEAPProgrammersManual (r1.3 - 08 Apr 2004 - PaulMarganian)
Changed:
<
<

Coming soon...

>
>

I obviously dont understand all the dynamics of the Panel/Site design, but, i've got code that works:

/users/pmargani/sandbox_trunk/sparrow/gbt/app/mydeap2

You're welcomed to try and make sense of it.

Here's my sad attempt at explaining it:

Providing the framework for the new Deap Panel

To use the Site/Panel mechanics used by Deap, instead of using wxApps and wxFrames, you use Deap's special classes in gui.framework: Application, Frame, Panel ...

Application:

Here's what a basic entry point for our new app looks like (mydeap.py):

#! /usr/bin/env python

import os
import sys

sys.path.insert(1, os.environ["DEAP"])

from   mydeapFrame  import mydeapFrame
from   gui.framework import Application
from   wxPython.wx   import *
import os.path

CONFIG_PATH = [
    os.path.expanduser("~/.sparrow")
  , os.path.join(os.environ["SPARROW_DIR"], "sparrow.conf")
]

class mydeap(Application):
    
    def __init__(self, console, configPath = CONFIG_PATH):
        Application.__init__(self, console, configPath)
        
    def OnInit(self):
        Application.OnInit(self)
        self.frame = mydeapFrame(None, -1, "test app")
        self.frame.Show(1)
        self.SetTopWindow(self.frame)
        return 1
        
def main(console):
    app = mydeap(console)
    app.MainLoop()
    
if __name__ == '__main__':
    console    = 1
    main(console)

Frame:

The class mydeapFrame is a subclass of Frame (as mydeap is a sub-class of Application):

class mydeapFrame(Frame):
    
    def __init__(self, parent, id, title):
        Frame.__init__(self, parent, id, title, size = (800, 600))
        self.InitDEAP()
        self.InitSplitter()
        self.InitPanel()

InitDEAP?() sets up our means for using command line commands for Deap (i'll get to mydeapDocument in a minute):

    def InitDEAP(self):
        "Construct an interpreter for our shell window."
        print 'creating deap interpreter from new document'
        self.interpreter = Interpreter(mydeapDocument())

InitSplitter?() sets up splitter seperating our command line Py shell, a console window for the actual Py shell for sending command line arguments to Deap, another console window for sending standard output to, and finally our panel that will actually hold the plot.

I'll get to the mydeapPanel class in a minute, but that's where the action is.

    def InitSplitter(self):
        "Split the frame with graphical panes on top and text below."
        self.outer    = wxSplitterWindow(self, -1)
        self.notebook = self.InitNotebook(self.outer) # notebook for py shell and console window
        self.console  = self.InitConsole(self.notebook)
        self.deapPanel = mydeapPanel(self.outer, self.interpreter, self.console) # our deap implementation
        self.outer.SplitHorizontally(self.deapPanel, self.notebook, -150)
        self.outer.SetMinimumPaneSize(20)

Introducing a new widget and menu item using the Document

With the framework of the application now in place, we can implement our own Deap panel.

Our first goal is to introduce a new widget (a checkbox) and a corresoponding check menu item. To do this we create a sub-class of Panel (also from gui.framework):

Check out the comments. You can see that this Panel is the wrapper for a plotting panel, and another panel for our new widget.

class mydeapPanel(Panel):
    
    def __init__(self, parent, interpreter, console):
        Panel.__init__(self, parent)

        # for using command lines
        self.interpreter = interpreter
        self.console     = console
        self.logFile     = None

        # this is the subject in the observer pattern
        self.mydoc = mydeapDocument()
        
        # create the panel for plotting
        self.plotPanel = DEAPPanel(self,self.mydoc) 
     
        # create the panel for our new widget
        self.controlPanel = controlPanel(self,-1,self.mydoc) 
 
        # subscribe to changes made to the document
        self.mydoc.AddObserver(self.OnUpdate)

        self.ActivatePanel(self.plotPanel)

Before I get to the mydeapDocument class, I should explain that this Panel is the Panel in the Panel/Site relationship. What's the that. Each Panel can set the apperance of it's site. In this case, the site is the mydeapFrame class. In brief, this Panel will set the Frame's new menu items.

Here's what happens:

  1. in mydeapPanel.__init__, ActivatePanel? is called, using the Deap panel.
  2. This causes the ActivateSite? method in mydeapPanel to be called
  3. In ActivateSite?, the panel gets the sites View menu bar
  4. It adds a new menu item, getting this menu's item from the state stored in the Document

The mydeapDocument class is sub-classed from document.Document, and is the subject in the observer pattern. That's where we store the state of our application. When changes are made to the document, all the 'observers', including this Panel, are sent updates (using Events - I'll get there).

Here's the document:

class mydeapDocument(Document):

    def __init__(self):
        Document.__init__(self)
        self.viewAll = 1
        self.viewLast = 0

    def SetViewAllOn(self):
        self.viewAll = 1
        self.viewLast = 0
       
        self.Update(ViewAllEvent())
        self.RePlot()

Here's what happens when the new menu item is checked:

  1. In the menu item event handler we call the appropriate mydeapDocument method directly (mydeapDocument.SetViewAllOn()). We dont use 'Action' classes becasue we aren't interested in undoing/redoing.
  2. The document toggles the two state flags, and calls the Update method using teh ViewAllEvent? class.
  3. The ViewAllEvent? is a subclass of event.Event and it's only current purpose is to uniquely identify the event as a 'ViewAll'.
  4. All objects that subcribed to the document, get an update, with id 'ViewAll'. In this case, our control panel get's this event, and, from the id of the event knows to toggle it's checkbox.

Here are the code snippets:

ViewAllEvent?.py:

from event.Event import *

class ViewAllEvent(Event):
    """
    This class is a container for information contained in a "set last data"
    event.
    """

    def __init__(self):
        "Initialize the event for when a title is set."

        Event.__init__(self, 38)

Control panel get's the update:

    def OnUpdate(self, event):
        if event.GetId() == 38: #view all set
            self.viewAll.SetValue(1)
            self.viewLast.SetValue(0)


 <<O>>  Difference Topic DEAPProgrammersManual (r1.2 - 06 Apr 2004 - PaulMarganian)
Added:
>
>

Creating a strip chart with DEAP

Coming soon...


 <<O>>  Difference Topic DEAPProgrammersManual (r1.1 - 06 Apr 2004 - AmyShelton)
Added:
>
>

%META:TOPICINFO{author="AmyShelton" date="1081281480" format="1.0" version="1.1"}% %META:TOPICPARENT{name="DEAPExamples"}%

Programmer's Manual for the Data Extraction and Analysis Program (DEAP)

  Main     Usage     Programmer's Manual     Requests     Updates     Examples  


In the works...


-- AmyShelton - 06 Apr 2004


Topic DEAPProgrammersManual . { View | Diffs | r1.8 | > | r1.7 | > | r1.6 | More }
Revision r1.1 - 06 Apr 2004 - 19:58 GMT - AmyShelton
Revision r1.8 - 13 May 2004 - 19:42 GMT - AmyShelton
Content copyright © 1999-2007 by the contributing authors.
All material on this collaboration platform is the property of the contributing authors.