GENESIS: Documentation

Related Documentation:

G3Plot.py

Enhanced version of plotVm that wraps Matplotlib plots within a wxPython GUI

 
#!/usr/bin/env python  
 
# A prototype of a stand-alone application for plotting the output of  
# GENESIS 3 models.  This uses a basic wxPython frame to hold a matplotlib  
# figure for plotting.  It defines some basic menu items, and a control  
# panel of buttons and toggles, each with bindings to a function to execute  
# on a mouse click.  It is based on the example program wx_mpl_bars.py and  
# other wxPython and matplotlib examples.  
 
import sys, os, glob  
 
# import needed wxPython modules  
import wx  
import wx.html  
import  wx.lib.dialogs  
 
import matplotlib  
matplotlib.use(’WXAgg’)  
from matplotlib.figure import Figure  
from matplotlib.backends.backend_wxagg import \  
    FigureCanvasWxAgg as FigCanvas, \  
    NavigationToolbar2WxAgg as NavigationToolbar  
import matplotlib.font_manager as font_manager  
 
import numpy as np  
 
class MainApp(wx.App):  
    def OnInit(self):  
       frame = PlotFrame("GENESIS 3 Plot Demo", (50, 60), (640, 640))  
       frame.Show()  
       self.SetTopWindow(frame)  
       return True  
 
class PlotFrame(wx.Frame):  
    """  
        PlotFrame is a custom wxPython frame to hold the panel with a  
Figure and WxAgg backend canvas for matplotlib plots or other  
        figures.  In this frame:  
 
        self is an instance of a wxFrame;  
        axes is an instance of MPL Axes;  
        fig is an instance of MPL Figure;  
        panel is an instance of wxPanel, used for the main panel, to hold  
        canvas, an instance of MPL FigureCanvasWxAgg.  
    """  
    def __init__(self, title, pos, size):  
        wx.Frame.__init__(self, None, wx.ID_ANY, title, pos, size)  
        # define some variables to be shared among functions below  
        # default filename list will be either empty or from args  
        self.filenames = sys.argv[1:]  
        # python 2.5 and later hack to remove duplicates: list to set to list  
        self.filenames = list(set(self.filenames))  
        self.plot_type = ’generic’  
        # format string for plot - default is color black  
        self.plot_format = ’k’  
 
        #    Make the main Matplotlib panel for plots  
        self.create_main_panel()  # creates canvas  
 
        #  
        # Layout with box sizers  
        #  
        self.sizer = wx.BoxSizer(wx.VERTICAL)  
        self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.EXPAND)  
        self.sizer.AddSpacer(10)  
        self.sizer.Add(self.toolbar, 0, wx.EXPAND)  
        self.sizer.AddSpacer(10)  
 
        #    Make the control panel with a row of buttons  
        self.create_button_bar()  
        self.sizer.Add(self.button_bar_sizer, 0, flag = wx.ALIGN_CENTER | wx.TOP)  
 
        #    Make a Status Bar  
        self.statusbar = self.CreateStatusBar()  
        self.sizer.Add(self.statusbar, 0, wx.EXPAND)  
 
        self.SetStatusText("Frame created ...")  
 
 
        # -------------------------------------------------------  
        #              set up the Menu Bar  
        # -------------------------------------------------------  
        menuBar = wx.MenuBar()  
 
        menuFile = wx.Menu() # File menu  
        menuFile.Append(1, "&Open", "Filename(s) or wildcard list to plot")  
        menuFile.Append(3, "Save", "Save plot as a PNG image")  
        menuFile.AppendSeparator()  
        menuFile.Append(10, "E&xit")  
        menuBar.Append(menuFile, "&File")  
 
        menuHelp = wx.Menu() # Help menu  
        menuHelp.Append(11, "&About Basic Plot")  
        menuHelp.Append(12, "&Usage and Help")  
        menuHelp.Append(13, "Program &Info")  
 
        menuBar.Append(menuHelp, "&Help")  
        self.SetMenuBar(menuBar)  
 
        self.panel.SetSizer(self.sizer)  
        self.sizer.Fit(self)  
 
        # -------------------------------------------------------  
        #      Bind the menu items to functions  
        # -------------------------------------------------------  
 
        self.Bind(wx.EVT_MENU, self.OnOpen, id=1)  
        self.Bind(wx.EVT_MENU, self.OnSave, id=3)  
        self.Bind(wx.EVT_MENU, self.OnQuit, id=10)  
        self.Bind(wx.EVT_MENU, self.OnAbout, id=11)  
        self.Bind(wx.EVT_MENU, self.OnUsage, id=12)  
        self.Bind(wx.EVT_MENU, self.OnInfo, id=13)  
        self.set_plot_type()  
 
        # ---------- end of __init__ ----------------------------  
 
    # -------------------------------------------------------  
    #   Function to make the main Matplotlib panel for plots  
    # -------------------------------------------------------  
    def create_main_panel(self):  
        """  
        create_main_panel creates the main panel with:  
                *  mathplotlib canvas  
                *  mathplotlib navigation toolbar  
        """  
        self.panel = wx.Panel(self)  
 
        # Create the mpl Figure and FigCanvas objects.  
        # 5x4 inches, 100 dots-per-inch  
        #  
        self.dpi = 100  
        self.fig = Figure((5.0, 4.0), dpi=self.dpi)  
        self.canvas = FigCanvas(self.panel, wx.ID_ANY, self.fig)  
 
        # Since we have only one plot, we could use add_axes  
        # instead of add_subplot, but then the subplot  
        # configuration tool in the navigation toolbar wouldn’t work.  
        #  
        # (111) == (1,1,1) --> row 1, col 1, Figure 1)  
        self.axes = self.fig.add_subplot(111)  
        #  
        # Create the navigation toolbar, tied to the canvas  
        #  
        self.toolbar = NavigationToolbar(self.canvas)  
 
    def create_button_bar(self):  
        """  
        create_button_bar makes a control panel bar with buttons and  
        toggles for  
 
        Clear - Plot - Overlay ON/OFF - generic/Vm plot - Legend OFF/On  
 
        It does not create a Panel container, but simply creates Button  
        objects with bindings, and adds them to a horizontal BoxSizer  
        self.button_bar_sizer.  This is added to the PlotFrame vertical  
        BoxSizer during initialization of the frame.  
 
        """  
        clear_button = wx.Button(self.panel, -1, "Clear")  
        self.Bind(wx.EVT_BUTTON, self.OnClear, clear_button)  
 
        replot_button = wx.Button(self.panel, -1, "Plot")  
        self.Bind(wx.EVT_BUTTON, self.OnReplot, replot_button)  
 
        # The toggle buttons need to be globally accessible  
        self.overlay_button = wx.ToggleButton(self.panel, -1, " Overlay ON ")  
        self.overlay_button.SetValue(True)  
        # the default is self.axes.hold(True)  
        self.overlay_button.SetLabel(" Overlay ON ")  
        self.Bind(wx.EVT_TOGGLEBUTTON, self.OnOverlay, self.overlay_button)  
 
        self.autoscale_button = wx.ToggleButton(self.panel, -1, " generic plot ")  
        self.autoscale_button.SetValue(False)  
        self.autoscale_button.SetLabel(" generic plot ")  
        self.Bind(wx.EVT_TOGGLEBUTTON, self.OnAutoscale, self.autoscale_button)  
 
        self.legend_button = wx.ToggleButton(self.panel, -1, " Legend OFF ")  
        self.legend_button.SetValue(False)  
        self.legend_button.SetLabel(" Legend OFF ")  
        self.Bind(wx.EVT_TOGGLEBUTTON, self.OnLegend, self.legend_button)  
 
        # info_button = wx.Button(self.panel, -1, "Info")  
        # self.Bind(wx.EVT_BUTTON, self.OnInfo, info_button)  
 
        # Set button colors to match G2 "colorize function" defaults  
        # This is highly dependent on X11 color definitions  
        clear_button.SetBackgroundColour(’rosybrown1’)  
        replot_button.SetBackgroundColour(’rosybrown1’)  
        self.overlay_button.SetForegroundColour(’red’)  
        self.overlay_button.SetBackgroundColour(’cadetblue1’)  
        self.autoscale_button.SetForegroundColour(’blue’)  
        self.autoscale_button.SetBackgroundColour(’cadetblue1’)  
        self.legend_button.SetForegroundColour(’blue’)  
        self.legend_button.SetBackgroundColour(’cadetblue1’)  
 
        self.button_bar_sizer = wx.BoxSizer(wx.HORIZONTAL)  
        flags = wx.ALIGN_CENTER | wx.ALL  
        self.button_bar_sizer.Add(clear_button, 0, border=3, flag=flags)  
        self.button_bar_sizer.Add(replot_button, 0, border=3, flag=flags)  
        self.button_bar_sizer.Add(self.overlay_button, 0, border=3, flag=flags)  
        self.button_bar_sizer.Add(self.autoscale_button, 0, border=3, flag=flags)  
        self.button_bar_sizer.Add(self.legend_button, 0, border=3, flag=flags)  
 
    # -------------------------------------------------------  
    #   Functions to generate or read (x,y) data and plot it  
    # -------------------------------------------------------  
 
    def plot_file(self):  
        # print ’Plotting %s’ % self.file  
        x = []; y = []  
        fp = open(self.file, ’r’)  
        # print ’Opened %s’ % self.file  
        for line in fp.readlines():  
            data = line.split(" ")  
            x.append(data[0]); y.append(data[1])  
        self.axes.plot(x, y, self.plot_format)  
 
    def plot_data_files(self):  
        formats = [’k’, ’r’, ’b’, ’g’, ’m’, ’c’]  
        plotnum = 0  
        if len(self.filenames) > 0:  
            for self.file in self.filenames:  
                self.plot_format = formats[plotnum % len(formats)]  
                try:  
                    if os.path.exists(self.file):  
                        self.plot_file()  
                        plotnum += 1  
                    else:  
                        print ’*** Error: Incorrect file name or path specified’  
                # I need to do better error handling!  
                except:  
                    print ’An error ocurred’  
                    # sys.exit()  
        else:  
            # bring up a warning dialog  
                msg = """  
                No existing files were specified for plotting!  
 
                Please enter one or more files to plot with File/Open.  
                """  
                wx.MessageBox(msg, "Plot Warning", wx.OK | wx.ICON_ERROR,self)  
 
 
    #  ---------------------------------------------------------------  
    #   Define the functions executed on menu choices  
    #  ---------------------------------------------------------------  
    def OnQuit(self, event):  
        self.Close()  
 
    def OnSave(self, event):  
        file_choices = "PNG (*.png)|*.png"  
        dlg = wx.FileDialog(  
            self,  
            message="Save plot as...",  
            defaultDir=os.getcwd(),  
            defaultFile="plot.png",  
            wildcard=file_choices,  
            style=wx.SAVE)  
 
        if dlg.ShowModal() == wx.ID_OK:  
            path = dlg.GetPath()  
            self.canvas.print_figure(path, dpi=self.dpi)  
            self.flash_status_message("Saved to %s" % path)  
 
    def OnOpen(self, event):  
        dlg = wx.TextEntryDialog(self, "File(s) with x,y data to plot",  
            "File Open", "Vm.out", style=wx.OK|wx.CANCEL)  
        if dlg.ShowModal() == wx.ID_OK:  
            filename_string = dlg.GetValue()  
            # print "You entered: %s" % filename_string  
            # expand wildcarded file list in filename_string  
            # For now, replace and not add to filenames - RadioButtons later?  
            self.filenames = []  
            if filename_string != "":  
                for name in filename_string.split():  
                    self.filenames += glob.glob(name)  
            files_plotted = ""  
            # python 2.5 and later remove duplicates: list to set to list  
            self.filenames = list(set(self.filenames))  
            for name in self.filenames:  
                files_plotted += " " + name  
            self.SetStatusText("Plotting " + files_plotted)  
            # print "filenames = ", self.filenames  
        dlg.Destroy()  
 
    def OnAbout(self, event):  
        msg = """  
                       G3Plot ver. 0.5  
 
G3Plot is a protoype of a stand-alone application for  
plotting the output of GENESIS 3 simulations. It defines a  
custom wxPython frame class PlotFrame to embed a matplotlib  
figure for plotting. PlotFrame defines some basic menu items  
on the menu bar, and a control panel of colored buttons and  
toggles, with bindings to functions executed on a mouse  
click.  The functions for the Help menu choices provide  
examples of providing documentation through the wxPython  
classes MessageDialog, ScrolledMessageDialog, Dialog, and  
HtmlWindow, for HTML-formatted documentation.  
 
Help/Usage gives HTML help for using G3Plot to plot data  
files.  This is the main Help page.  
 
Help/Program Info provides some information about the  
objects and functions, and the wxPython and matplotlib  
classes used here.  
 
Dave Beeman, April 2010  
        """  
        dlg = wx.MessageDialog(self, msg, "About G3 Plot and PlotFrame",  
            wx.OK | wx.ICON_QUESTION)  
dlg.ShowModal()  
dlg.Destroy()  
 
    class UsageFrame(wx.Frame):  
        text = """  
<HTML>  
<HEAD></HEAD>  
<BODY BGCOLOR="#D6E7F7">  
 
<CENTER><H1>Using G3 Plot and PlotFrame</H1></CENTER>  
 
<H2>Introduction and Quick Start</H2>  
 
<P> PlotFrame is a prototype of a Python class for use in an application  
for plotting the output of GENESIS 3 simulations. It uses a basic wxPython  
frame to embed a matplotlib figure for plotting.  It defines some basic  
menu items and a control panel of buttons and toggles, each with bindings  
to a function to execute on a mouse click, and can aso be used as an  
example of embedding Matplotlib plots within a wx Widgets environment.  
 
<P>  
 
G3Plot is used to plot multiple files, with a number of features designed  
to simplify the process of comparing multiple plots of membrane potential.  
You may specify a list of one or more files that each contain multiple  
lines of (x,y) data pairs separated by white space.  The file list may  
contain wildards, and can be specified either on the command line, e.g.:  
 
<PRE>  
      G3Plot Vm.out Vm_data/pyr*.out  
</PRE>  
 
or in the dialog brought up from the File/Open menu.  The wildcards will be  
expanded and paths to the matching files will go into a <i>filenames</i>  
list, which has any duplicate wildcard matches removed.  
 
Then, click on the Plot button in the Control Button Bar to plot the data  
in the file or files in the <i>filenames</i> list.  
 
<H2>Menu Bar Choices</H2>  
 
<UL>  
  <LI><B>File</B>  
  <UL>  
    <LI><B>Open</B> - Enter the filename(s) or wildcarded list of files to  
    plot.  
 
    <LI><B>Save</B> - Set filename, browse for directory, and save the  
   current plot in a PNG format file.  
 
    <LI><B>Exit</B>  - Exit the program  
 
  </UL>  
  <LI><B>Help</B>  
 
  <UL>  
    <LI><B>About</B> - Brief description of this application  
 
    <LI><B>Usage</B> - Main Help and Usage Information  
 
    <LI><B>Program Info</B> - Some details of the implementation, obtained  
           from internal documentation strings  
 
  </UL>  
</UL>  
<H2>Control Button Bar</H2>  
 
<UL>  
  <LI><B>[Clear]</B> - Clear the plot  
 
  <LI><B>[Plot]</B> - Plot the data in the file(s) that are in the  
      <i>filenames</i> list  
 
  <LI><B>[Overlay ON/OFF]</B> - When ON (the default), multiple files in the  
  <i>filenames</i> list will be plotted on the same axes without clearing  
  the plot.  When OFF, the plot is cleared after each file is plotted.  
 
  <LI><B>[generic/Vm plot]</B> - When set to ’generic plot’ , the title and  
  axis labels are set for a generic (x,y) plot.  When set to ’Vm plot’, the  
  title and axis labels are set to values appropriate for membrane  
  potential (volts) vs time (seconds).  
 
  <LI><B>[Legend ON/OFF]</B> - Toggle the plot legend on and off.  The  
  legend is made from the <i>filenames</i> list, using the colors of the  
  corresponding line plot.  The legend will not be displayed if the  
  currently displayed plot does not have the same number of line plots as  
  there are files in the list, e.g. it will not attempt to show the legend  
  after clearing the plot, and will pop up a warning dialog.  
 
</UL>  
<H2>Matplotlib Navigation Panel</H2>  
 
<P>  
Navigation within the current plot is performed using the seven icons  
displayed in the panel below the plot.  
 
<P>  
The Home, Back, and Forward buttons are used to navigate back and forth  
between previously defined views.  You can create a new view of your data  
by using the Pan or Zoom buttons.  
 
<UL>  
  <LI><B>[ |ˆ| ] Home</B>  - Go to the first, default view  
 
  <LI><B>[ &lt;-- ] Back</B> - Go to previous view, if one exists  
 
  <LI><B>[ --&gt; ] Forward</B> - Go to next view, if one exists  
 
  <LI><B>[ + ] Pan</B> - When this button is activated, click left mouse  
  button and drag to pan the figure, dragging it to a new position.  
 
  <LI><B>[ O ] Zoom</B> - When this button is activated, click left mouse  
  button and drag to define a zoom region.  
 
  <LI><B>[  #  ] Configure Subplots</B> - configure the parameters of the plot: the  
 left, right, top, bottom, space between the rows and space between  
 the columns  
 
  <LI><B>[ disk ] Save</B> - Launch a file save dialog for the plot  
 
</UL>  
<HR>  
</BODY>  
</HTML>  
        """  
        def __init__(self, parent):  
            wx.Frame.__init__(self, parent, -1, "Usage and Help",  
                size=(640,600), pos=(400,100))  
            html = wx.html.HtmlWindow(self)  
            html.SetPage(self.text)  
            panel = wx.Panel(self, -1)  
            button = wx.Button(panel, wx.ID_OK, "Close")  
            self.Bind(wx.EVT_BUTTON, self.OnCloseMe, button)  
            sizer = wx.BoxSizer(wx.VERTICAL)  
            sizer.Add(html, 1, wx.EXPAND|wx.ALL, 5)  
            sizer.Add(panel, 0, wx.ALIGN_CENTER|wx.ALL, 5)  
            self.SetSizer(sizer)  
            self.Layout()  
 
        def OnCloseMe(self, event):  
            self.Close(True)  
 
    def OnUsage(self,event):  
        usagewin = self.UsageFrame(self)  
        usagewin.Show(True)  
 
    def OnInfo(self,event):  
        msg = "Program information for PlotFrame obtained from docstrings:"  
        msg += "\n" + self.__doc__ + "\n" + self.create_main_panel.__doc__  
        msg += self.create_button_bar.__doc__  
        dlg = wx.lib.dialogs.ScrolledMessageDialog(self, msg,  
            "PlotFrame Documentation")  
dlg.ShowModal()  
 
    #  ---------------------------------------------------------------  
    #   Define the functions executed on control button click  
    #  ---------------------------------------------------------------  
    def OnClear(self,event):  
        self.axes.clear()  
        self.canvas.draw()  
        # self.Refresh()  
 
    def OnReplot(self,event):  
        self.plot_data_files()  
        self.canvas.draw()  
 
    def OnOverlay(self,event):  
        state =  self.overlay_button.GetValue()  
        if state:  
            # print state  
            self.overlay_button.SetLabel("Overlay ON")  
            self.overlay_button.SetForegroundColour(’red’)  
            self.axes.hold(True)  
        else:  
            # print state  
            self.overlay_button.SetLabel("Overlay OFF")  
            self.overlay_button.SetForegroundColour(’blue’)  
            self.axes.hold(False)  
 
    def OnAutoscale(self,event):  
state =  self.autoscale_button.GetValue()  
        if state:  
            self.autoscale_button.SetLabel("   Vm plot  ")  
            self.autoscale_button.SetForegroundColour(’red’)  
            self.plot_type = ’Vm’  
        else:  
            self.autoscale_button.SetLabel("generic plot")  
            self.autoscale_button.SetForegroundColour(’blue’)  
            self.plot_type = ’generic’  
        self.set_plot_type()  
 
    def set_plot_type(self):  
        # print self.plot_type  
        if self.plot_type == ’generic’:  
            self.axes.set_autoscale_on(True)  
            self.axes.set_title(’(x,y) data’)  
            self.axes.set_xlabel(’X’)  
            self.axes.set_ylabel(’Y’)  
            self.canvas.draw()  
            # self.Refresh()  
        else:  
            self.axes.set_autoscale_on(False)  
 
            self.axes.set_title(’Membrane Potential’)  
            self.axes.set_xlabel(’seconds’)  
            self.axes.set_ylabel(’Volts’)  
            self.axes.axis(ymin=-0.1, ymax=0.05)  
            self.canvas.draw()  
            # self.Refresh()  
 
    def OnLegend(self,event):  
        state =  self.legend_button.GetValue()  
        if state:  
            self.legend_button.SetLabel("Legend ON")  
            self.legend_button.SetForegroundColour(’red’)  
            # Need to check that number in list == number of lines on plot  
            # Could still be problems if plot is cleared and no filenames  
            nlines = len(self.axes.get_lines())  
            if (len(self.filenames) == nlines) and (nlines != 0):  
                # use filenames list for legend and reduce font size  
                self.axes.legend(self.filenames,  
                    prop=font_manager.FontProperties(size=10))  
            else:  
                msg = "The plot has " + str(len(self.axes.get_lines())) + \  
                " lines, but the filenames list has " + \  
                str(len(self.filenames)) + " entries.\n\n" + \  
                "Please enter one or more files to plot with File/Open, " + \  
                " click on Clear, and then Plot.  Then toggle to Legend ON."  
                wx.MessageBox(msg, "Legend Warning", wx.OK | wx.ICON_ERROR, self)  
            self.canvas.draw()  
        else:  
            self.legend_button.SetLabel("Legend OFF")  
            self.legend_button.SetForegroundColour(’blue’)  
            self.autoscale_button.SetForegroundColour(’blue’)  
            ax = self.fig.gca()  
            ax.legend_ = None  
            self.canvas.draw()  
 
    #  ---------------------------------------------------------------  
    #   Define some auxillary functions  
    #  ---------------------------------------------------------------  
    def flash_status_message(self, msg, flash_len_ms=1500):  
        self.statusbar.SetStatusText(msg)  
        self.timeroff = wx.Timer(self)  
        self.Bind(  
            wx.EVT_TIMER,  
            self.on_flash_status_off,  
            self.timeroff)  
        self.timeroff.Start(flash_len_ms, oneShot=True)  
 
    def on_flash_status_off(self, event):  
        self.statusbar.SetStatusText(’’)  
 
if __name__ == ’__main__’:  
    app = MainApp(False)  
    app.MainLoop()  
\