#!/usr/bin/env python

# Basic wxPython frame to hold a matplotlib figure for plotting
# It defines some basic menu items with bindings to functions to execute

import sys, os, glob, time, math, bz2

# import needed wxPython modules

import wx
import wx.html
import	wx.lib.dialogs
from wx.lib.stattext import GenStaticText

import matplotlib
import matplotlib.cm as cm
from matplotlib.widgets import Slider, Button

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 for fancy labeled text entry widget
class XDialog(wx.Panel):
    def __init__(self, parent, *args, **kwargs):
	wx.Panel.__init__(self, parent, *args, **kwargs)
	self.entry_label = GenStaticText(self, wx.ID_ANY,
            label=" default label ", size=(-1,30))
	# Use the default width (-1) and 30 pixel height
	# Set the background color to palegoldenrod
	self.entry_label.SetBackgroundColour('#EEE8AA')

	self.entry = wx.TextCtrl(self, wx.ID_ANY, '')
        self.entry.SetBackgroundColour('#EEE8AA')
	self.entry.SetWindowStyleFlag(wx.TE_PROCESS_ENTER)

        # Arrange the widgets in a horizontal BoxSizer
	dlg_sizer = wx.BoxSizer(wx.HORIZONTAL)

	dlg_sizer.Add(self.entry_label, 1, border=0)
	dlg_sizer.Add(self.entry, 1, wx.EXPAND, border=0)

	self.SetSizer(dlg_sizer)
    
# custom frame to hold the panel with a Figure and WxAgg backend canvas
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.
    """

    # Main function to set everything up when the frame is created
    def __init__(self, title, pos, size):

        """
           This will be executed when an instance of PlotFrame is created.
           It is the place to define any globals as "self.<name>".
        """
        wx.Frame.__init__(self, None, wx.ID_ANY, title, pos, size)

        if len(sys.argv) < 2:
            self.filename = ""
        else:
            self.filename =  sys.argv[1]

        # set some Boolean flags
        self.STOP = False
        self.data_loaded = False
        self.reverse_play = False

        self.step = 1

        #    Make the main Matplotlib panel for plots
        self.create_main_panel()  # creates canvas and contents

        # Then add wxPython widgets below the MPL 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 Netview")
        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)
	
        # methods defined below to get and plot the data
        # Normally do the plot on request, and not here
        # self.get_data_params()
        # self.init_plot()
        # self.get_xyt_data()
        # plot_data()

	# ---------- end of __init__ ----------------------------

    # -------------------------------------------------------
    #   Function to make the main Matplotlib panel for plots
    # -------------------------------------------------------

    def create_main_panel(self):
        """ create_main_panel creates the main mpl panel with instances of:
             * mpl Canvas 
             * mpl Figure 
             * mpl Figure
             * mpl Axes with subplot
             * mpl Widget class Sliders and Button
             * mpl navigation toolbar
           self.axes is the instance of MPL Axes, and is where it all happens
        """

        self.panel = wx.Panel(self)
        
        # Create the mpl Figure and FigCanvas objects. 
        # 3.5 x 5 inches, 100 dots-per-inch
        #
        self.dpi = 100
        self.fig = Figure((3.5, 5.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.


        self.axes = self.fig.add_subplot(111)
        # (111) == (1,1,1) --> row 1, col 1, Figure 1)
        # self.axes.set_title("View from: "+self.filename)

        # Now create some sliders below the plot after making room
        self.fig.subplots_adjust(left=0.1, bottom=0.20)

        self.axtmin = self.fig.add_axes([0.2, 0.10, 0.5, 0.03])
        self.axtmax = self.fig.add_axes([0.2, 0.05, 0.5, 0.03])

        self.stmin = Slider(self.axtmin, 't_min:', 0.0, 1.0, valinit = 0.0)
        self.stmax = Slider(self.axtmax, 't_max:', 0.0, 1.0, valinit = 1.0)
        self.stmin.on_changed(self.update_trange)
        self.stmax.on_changed(self.update_trange)

        self.axbutton = self.fig.add_axes([0.8, 0.07, 0.1, 0.07])
        self.reset_button = Button(self.axbutton, 'Reset')
        self.reset_button.color = 'skyblue'
        self.reset_button.hovercolor = 'lightblue'
        self.reset_button.on_clicked(self.reset_trange)

        # Create the navigation toolbar, tied to the canvas

        self.toolbar = NavigationToolbar(self.canvas)

    def update_trange(self, event):
        self.t_min = self.stmin.val
        self.t_max = self.stmax.val
        # print(self.t_min, self.t_max)

    def reset_trange(self, event):
        self.stmin.reset()
        self.stmax.reset()

    def create_button_bar(self):
	"""
	create_button_bar makes a control panel bar with buttons and
	toggles for

	New Data - Play - STOP - Single Step - Forward/Back - Normal/Fast

	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, after the MPL canvas, during initialization of the frame.

	"""
	rewind_button = wx.Button(self.panel, -1, "New Data")
	self.Bind(wx.EVT_BUTTON, self.OnRewind, rewind_button)

	replot_button = wx.Button(self.panel, -1, "Play")
	self.Bind(wx.EVT_BUTTON, self.OnReplot, replot_button)

	sstep_button = wx.Button(self.panel, -1, "Single Step")
	self.Bind(wx.EVT_BUTTON, self.OnSstep, sstep_button)

	stop_button = wx.Button(self.panel, -1, "STOP")
	self.Bind(wx.EVT_BUTTON, self.OnStop, stop_button)

	# The toggle buttons need to be globally accessible

	self.forward_toggle = wx.ToggleButton(self.panel, -1, "Forward")
	self.forward_toggle.SetValue(True)
	self.forward_toggle.SetLabel("Forward")
	self.Bind(wx.EVT_TOGGLEBUTTON, self.OnForward, self.forward_toggle)

	self.fast_toggle = wx.ToggleButton(self.panel, -1, " Normal ")
        self.fast_toggle.SetValue(True)
        self.fast_toggle.SetLabel(" Normal ")
        self.Bind(wx.EVT_TOGGLEBUTTON, self.OnFast, self.fast_toggle)

        # Set button colors to some simple colors that are likely
        # to be independent on X11 color definitions.  Some nice
        # bit maps (from a media player skin?) should be used
        # or the buttons and toggle state colors in OnFast() below

        rewind_button.SetBackgroundColour('skyblue')
        replot_button.SetBackgroundColour('skyblue')
        sstep_button.SetBackgroundColour('skyblue')
        stop_button.SetBackgroundColour('skyblue')
        self.forward_toggle.SetForegroundColour('black')
        self.forward_toggle.SetBackgroundColour('yellow')
        self.fast_toggle.SetForegroundColour('black')
        self.fast_toggle.SetBackgroundColour('yellow')
        self.button_bar_sizer = wx.BoxSizer(wx.HORIZONTAL)
        flags = wx.ALIGN_CENTER | wx.ALL
        self.button_bar_sizer.Add(rewind_button, 0, border=3, flag=flags)
        self.button_bar_sizer.Add(replot_button, 0, border=3, flag=flags)
        self.button_bar_sizer.Add(sstep_button, 0, border=3, flag=flags)
        self.button_bar_sizer.Add(stop_button, 0, border=3, flag=flags)
        self.button_bar_sizer.Add(self.forward_toggle, 0, border=3, flag=flags)
        self.button_bar_sizer.Add(self.fast_toggle, 0, border=3, flag=flags)

    # -------------------------------------------------------
    #	Functions to generate or read (x,y) data and plot it
    # -------------------------------------------------------

    def get_data_params(self):
        #  These parameters  would normally be provided in a file header,
        # past as arguments in a function, or from other file information
        #  Next version will bring up a dialog for dt NX NY if no file header

        # Here check to see if a filename should be entered from File/Open
        # self.filename = 'Ex_net_Vm_0001.txt'
        if len(self.filename) == 0:
            # fake a button press of File/Open
            self.OnOpen(wx.EVT_BUTTON)

        # should check here if file exists as specified [path]/filename

        # assume it is a bzip2 compressed file
        try:
      	    fp = bz2.BZ2File(self.filename)
	    line = fp.readline()
        except IOError:
            # then assume plain text
            fp = open(self.filename)
	    line = fp.readline()
       
        fp.close()

        # check if first line is a header line starting with '#'
        header = line.split()
        if header[0][0] == "#": 
            self.Ntimes = int(header[1])
            self.t_min =  float(header[2])
            self.dt = float(header[3])
            self.NX = int(header[4])
            self.NY = int(header[5])
        else:
            pdentry = self.ParamEntryDialog()
            if pdentry.ShowModal() ==  wx.ID_OK:
                self.Ntimes = int(pdentry.Ntimes_dialog.entry.GetValue())
		self.t_min = float(pdentry.tmin_dialog.entry.GetValue())
		self.dt = float(pdentry.dt_dialog.entry.GetValue())
                self.NX	= int(pdentry.NX_dialog.entry.GetValue())
                self.NY = int(pdentry.NY_dialog.entry.GetValue())
                print 'Ntimes = ', self.Ntimes, ' t_min = ', self.t_min
                print 'NX = ', self.NX, ' NY = ', self.NY
            pdentry.Destroy()
        self.t_max = (self.Ntimes - 1)*self.dt
        # reset slider max and min
        self.stmin.valmax = self.t_max
        self.stmin.valinit = self.t_min
        self.stmax.valmax = self.t_max
        self.stmax.valinit = self.t_max
        self.stmax.set_val(self.t_max)
        self.stmin.reset()
        self.stmax.reset()
        fp.close()

    def init_plot(self):
        ''' 
        init_plot creates the initial plot display. A normal MPL plot
	would be created here with a command "self.axes.plot(x, y)" in
	order to create a plot of points in the x and y arrays on the
	Axes subplot.  Here, we create an AxesImage instance with
	imshow(), instead.  The initial image is a blank one of the
	proper dimensions, filled with zeroes.

        '''
        self.t_max = (self.Ntimes - 1)*self.dt
        self.axes.set_title("View of "+self.filename)
        # Note that NumPy array (row, col) = image (y, x)
        data0 = np.zeros((self.NY,self.NX))

        # Define a 'cold' to 'hot' color scale based in GENESIS 2 'hot'
        hotcolors = ['#000032', '#00003c', '#000046',
        '#000050', '#00005a', '#000064', '#00006e', '#000078', '#000082',
        '#00008c', '#000096', '#0000a0', '#0000aa', '#0000b4', '#0000be',
        '#0000c8', '#0000d2', '#0000dc', '#0000e6', '#0000f0', '#0000fa',
        '#0000ff', '#000af6', '#0014ec', '#001ee2', '#0028d8', '#0032ce',
        '#003cc4', '#0046ba', '#0050b0', '#005aa6', '#00649c', '#006e92',
        '#007888', '#00827e', '#008c74', '#00966a', '#00a060', '#00aa56',
        '#00b44c', '#00be42', '#00c838', '#00d22e', '#00dc24', '#00e61a',
        '#00f010', '#00fa06', '#00ff00', '#0af600', '#14ec00', '#1ee200',
        '#28d800', '#32ce00', '#3cc400', '#46ba00', '#50b000', '#5aa600',
        '#649c00', '#6e9200', '#788800', '#827e00', '#8c7400', '#966a00',
        '#a06000', '#aa5600', '#b44c00', '#be4200', '#c83800', '#d22e00',
        '#dc2400', '#e61a00', '#f01000', '#fa0600', '#ff0000', '#ff0a00',
        '#ff1400', '#ff1e00', '#ff2800', '#ff3200', '#ff3c00', '#ff4600',
        '#ff5000', '#ff5a00', '#ff6400', '#ff6e00', '#ff7800', '#ff8200',
        '#ff8c00', '#ff9600', '#ffa000', '#ffaa00', '#ffb400', '#ffbe00',
        '#ffc800', '#ffd200', '#ffdc00', '#ffe600', '#fff000', '#fffa00',
        '#ffff00', '#ffff0a', '#ffff14', '#ffff1e', '#ffff28', '#ffff32',
        '#ffff3c', '#ffff46', '#ffff50', '#ffff5a', '#ffff64', '#ffff6e',
        '#ffff78', '#ffff82', '#ffff8c', '#ffff96', '#ffffa0', '#ffffaa',
        '#ffffb4', '#ffffbe', '#ffffc8', '#ffffd2', '#ffffdc', '#ffffe6',
        '#fffff0']

        cmap = matplotlib.colors.ListedColormap(hotcolors)

        self.im = self.axes.imshow(data0, cmap=cmap, origin='lower')

        # http://matplotlib.sourceforge.net/examples/pylab_examples/show_colormaps.html
        # shows examples to use as a 'cold' to 'hot' mapping of value to color
        # cm.jet, cm.gnuplot and cm.afmhot are good choices, but are unlike G2 'hot'

        self.im.cmap=cmap

        # Not sure how to properly add a colorbar
        # self.cb = self.fig.colorbar(self.im, orientation='vertical')

        # refresh the canvas
        self.canvas.draw()      

    def get_xyt_data(self):
        # Create scaled (0-1) luminance(x,y) array from ascii G-2 disk_out file
        # get the data to plot from the specified filename
        # Note that NumPy loadtxt transparently deals with bz2 compression
        self.SetStatusText('Data loading - please wait ....')
        rawdata = np.loadtxt(self.filename)
        # Note the difference between NumPy [row, col] order and network
        # x-y grid (x, y) = (col, row). We want a NumPy NY x NX, not 
        # NX x NY, array to be used by the AxesImage object.

        xydata = np.resize(rawdata, (self.Ntimes, self.NY, self.NX))
        # imshow expects the data to be scaled to range 0-1.
        Vmin = xydata.min()
        Vmax = xydata.max()
        print 'Vmax = ', Vmax
        # Hack for EPSC data - most points are < 2 nA, but a few are large
        # Scale data to a more reasonable range
        Vmax = 2.0e-09
        self.ldata = (xydata - Vmin)/(Vmax - Vmin)
        self.data_loaded = True
        self.SetStatusText('Data has been loaded - click Play')

    def plot_data(self):
        ''' plot_data() shows successive frames of the data that was loaded
            into the ldata array.  Creating a new self.im AxesImage instance
            for each frame is extremely slow, so the set_data method of
            AxesImage is used to load new data into the existing self.im for
            each frame.  Normally 'self.canvas.draw()' would be used to
            display a frame, but redrawing the entire canvas, redraws the
            axes, labels, sliders, buttons, or anything else on the canvas.
            This uses a method taken from an example in Ch 7, p. 192
            Matplotlib for Python developers, with draw_artist() and blit()
            redraw only the part that was changed.

        '''
        if self.data_loaded == False:
            # bring up a warning dialog
            msg = """
            Data for plotting has not been loaded!
            Please enter the file to plot with File/Open, unless
            it was already specified, and then click on 'New Data'
            to load the data to play back, before clicking 'Play'.
            """
            wx.MessageBox(msg, "Plot Warning", wx.OK | wx.ICON_ERROR,self)
            return

        # set color limits
        self.im.set_clim(0.0, 1.0)
        self.im.set_interpolation('nearest')
        # 'None' is is slightly faster, but not implemented for MPL ver < 1.1
        # self.im.set_interpolation('None')

        # do an initial draw, then save the empty figure axes
        self.canvas.draw()      

        # self.bg = self.canvas.copy_from_bbox(self.axes.bbox)
        # However the save and restore is only  needed if I change
        # axes legends, etc.  The draw_artist(artist), and blit
        # are much faster than canvas.draw() and are sufficient.

        print 'system time (seconds) = ', time.time()

        # round frame_min down and frame_max up for the time window
        frame_min = int(self.t_min/self.dt)
        frame_max = min(int(self.t_max/self.dt) + 1, self.Ntimes)
        frame_step = self.step

        # Displaying simulation time to the status bar is much faster
        # than updating a slider progress bar, but location isn't optimum.
        # The check for the STOP button doesn't work because the button
        # click is not registered until this function exits.

        # check to see if self.reverse_play == True
        # then interchange frame_min, frame_max, and use negative step
        if self.reverse_play == True:
            frame_min = min(int(self.t_max/self.dt) + 1, self.Ntimes) - 1
            frame_max = int(self.t_min/self.dt) - 1
            frame_step = -self.step
        for frame_num in range(frame_min, frame_max, frame_step):
            self.SetStatusText('time: '+str(frame_num*self.dt))
            if self.STOP == True:
                self.t_min = frame_num*self.dt
                # set t_min slider ?
                self.STOP = False
                break
            self.im.set_data(self.ldata[frame_num])
            self.axes.draw_artist(self.im)
            self.canvas.blit(self.axes.bbox)

        print 'system time (seconds) = ', time.time()

    #  ------------------------------------------------------------------
    #	Define the classes and functions for getting parameter values
    #  --------------------------------------------------------------

    class ParamEntryDialog(wx.Dialog):
        def __init__(self):
	    wx.Dialog.__init__(self, None, wx.ID_ANY)
	    self.SetSize((250, 200))
	    self.SetTitle('Enter Data File Parameters')
	    vbox = wx.BoxSizer(wx.VERTICAL)
	    self.Ntimes_dialog = XDialog(self)
	    self.Ntimes_dialog.entry_label.SetLabel('Number of entries')
	    self.Ntimes_dialog.entry.ChangeValue(str(2501))
	    self.tmin_dialog = XDialog(self)
	    self.tmin_dialog.entry_label.SetLabel('Start time (sec)')
	    self.tmin_dialog.entry.ChangeValue(str(0.0))

	    self.dt_dialog = XDialog(self)
	    self.dt_dialog.entry_label.SetLabel('Output time step (sec)')
	    self.dt_dialog.entry.ChangeValue(str(0.0002))

	    self.NX_dialog = XDialog(self)
	    self.NX_dialog.entry_label.SetLabel('Number of cells on x-axis')
            self.NX_dialog.entry.ChangeValue(str(32))
            self.NY_dialog = XDialog(self)
            self.NY_dialog.entry_label.SetLabel('Number of cells on y-axis')
            self.NY_dialog.entry.ChangeValue(str(32))

            vbox.Add(self.Ntimes_dialog, 0, wx.EXPAND|wx.ALL, border=5)
            vbox.Add(self.tmin_dialog, 0, wx.EXPAND|wx.ALL, border=5)
            vbox.Add(self.dt_dialog, 0, wx.EXPAND|wx.ALL, border=5)
            vbox.Add(self.NX_dialog, 0, wx.EXPAND|wx.ALL, border=5)
            vbox.Add(self.NY_dialog, 0, wx.EXPAND|wx.ALL, border=5)

            okButton = wx.Button(self, wx.ID_OK,'Ok')
	    # vbox.Add(okButton,flag=wx.ALIGN_CENTER|wx.TOP|wx.BOTTOM, border=10)
	    vbox.Add(okButton,flag=wx.ALIGN_CENTER, border=10)

            self.SetSizer(vbox)
	    self.SetSizerAndFit(vbox)

    #  ------------------------------------------------------------------
    #	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 OnAbout(self, event):
	msg = """

                      G-3 Netview ver. 1.7

Netview is a stand-alone Python application for viewing
the output of GENESIS 2 and 3 network simulations.
It is intended to replace GENESIS 2 SLI scripts that use the
XODUS 'xview' widget.

The design and operation is based on the G3Plot application
for creating 2D plots of y(t) or y(x) from data files.
Unlike G3Plot, the image created with Netview is an animated
representation of a rectangular network with colored squares
used to indicate the value of some variable at that position
and time.  Typically, this would be the membrane potenial of
a cell soma, or a synaptic current in a dendrite segment.

Help/Usage gives HTML help for using Netview.
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, August 2012
	"""
        dlg = wx.MessageDialog(self, msg, "About G-3 Netview",
            wx.OK | wx.ICON_QUESTION)
	dlg.ShowModal()
	dlg.Destroy()

    def OnOpen(self, event):
        dlg = wx.TextEntryDialog(self, "File with x,y data to plot",
	    "File Open", self.filename, style=wx.OK|wx.CANCEL)
        if dlg.ShowModal() == wx.ID_OK:
            self.filename = dlg.GetValue()
            # A new filename has been entered, but the data has not been read
            self.data_loaded = False
            # print "You entered: %s" % self.filename     
        dlg.Destroy()

    #  This starts with the long string of HTML to display
    class UsageFrame(wx.Frame):
        text = """
<HTML>
<HEAD></HEAD>
<BODY BGCOLOR="#D6E7F7">

<CENTER><H1>Using G-3 Netview</H1></CENTER>

<H2>Introduction and Quick Start</H2>

<p>Netview is a stand-alone Python application for viewing the output of
GENESIS 2 and 3 network simulations.  It is intended to replace GENESIS 2
SLI scripts that use the XODUS 'xview' widget.</p>

<p>The design and operation is based on the G3Plot application for creating 2D
plots of y(t) or y(x) from data files.  As with G3Plot, the main class
PlotFrame 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.</p>

<p>Unlike G3Plot, the image created with Netview is an animated
representation of a rectangular network with colored squares
used to indicate the value of some variable at that position
and time.  Typically, this would be the membrane potenial of
a cell soma, or a synaptic current in a dendrite segment.</p>

<h2>Usage</h2>

<p>The Menu Bar has <em>File/Open</em>, <em>File/Save</em>, and
<em>File/Exit</em> choices.  The Help Menu choices <em>About</em> and
<em>Usage</em> give further information.  The <em>Program Info</em>
selection shows code documentation that is contained in some of the main
function <em>docstrings</em>.</p>

<p>After starting the <em>netview</em> program, enter a data file name
in the dialog for File/Open, unless the filename was given as a
command line argument.  Then click on <strong>New Data</strong> to load the new
data and initialize the plot.  When the plot is cleared to black,
press <strong>Play</strong>.</p>

<p>The file types recognized are plain text or text files compressed with
bzip2.  The expected data format is one line for each output time step,
with each line having the membrane potential value of each cell in the net.
No time value should be given on the line.  In order to properly display
the data, netview needs some additional information about the network and
the data.  This can optionally be contained in a header line that precedes
the data.  If a header is not detected, a dialog will appear asking for the
needed parameters.</p>

<p>It is assumed that the cells are arranged on a NX x NY grid, numbered
from 0 (bottom left corner) to NX*NY - 1 (upper right corner).
In order to provide this information to netview, the data file should
begin with a header line of the form:</p>

<pre>
    #optional_RUNID_string Ntimes start_time dt NX NY SEP_X SEP_Y x0 y0 z0
</pre>

<p>The line must start with &quot;#&quot; and can optionally be followed immediately by any
string.  Typically this is some identification string generated by the
simulation run.  The following parameters, separated by blanks or any
whitespace, are:</p>

<ul>
<li>Ntimes - the number of lines in the file, exclusive of the header</li>
<li>start_time - the simulation time for the first data line (default 0.0)</li>
<li>dt - the time step used for output</li>
<li>NX, NY - the integer dimensions of the network</li>
<li>SEP_X, SEP_Y - the x,y distances between cells (optional)</li>
<li>x0, y0, z0 - the location of the compartment (data source) relative to the
cell origin</li>
</ul>

<p>The RUNID string and the last five parameters are not read or used
by netview.  These are available for other data analysis tools that
need a RUNID and the location of each source.</p>

<p>The slider bars can be used to set a time window for display, and the
<strong>Reset</strong> button can set t_min and t_max back to the defaults.
Use the <strong>Forward/Back</strong> toggle to reverse direction of
<strong>Play</strong>, and the <strong>Normal/Fast</strong> toggle to show
every tenth frame.</p> <p>The <strong>Single Step</strong> button can be
used to advance a single step at a time (or 10, if in 'Fast' mode).</p>

<p>The <strong>STOP</strong> button is currently not implemented</p>
<p>To plot different data, enter a new filename with <strong>File/Open</strong> and
repeat with <strong>New Data</strong> and <strong>Play</strong>.</p>

<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)

        # ----------- end of class UsageFrame ---------------

    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__
        msg += self.init_plot.__doc__
        msg += self.plot_data.__doc__
        dlg = wx.lib.dialogs.ScrolledMessageDialog(self, msg,
            "PlotFrame Documentation")
        dlg.ShowModal()

    #  ---------------------------------------------------------------
    #	Define the functions executed on control button click
    #  ---------------------------------------------------------------

    def OnRewind(self,event):
        self.get_data_params()
        self.init_plot()
        self.get_xyt_data()

    def OnReplot(self,event):
        self.plot_data()
        self.canvas.draw()

    def OnSstep(self,event):
        if self.data_loaded == False:
            # bring up a warning dialog
            msg = """
            Data for plotting has not been loaded!
            Please enter the file to plot with File/Open, unless
            it was already specified, and then click on 'New Data'
            to load the data to play back, before clicking 'Play'.
            """
            wx.MessageBox(msg, "Plot Warning", wx.OK | wx.ICON_ERROR,self)
            return

        self.t_max = min(self.t_max + self.dt, (self.Ntimes - 1)*self.dt)
        self.stmax.set_val(self.t_max)
        frame_num = int(self.t_max/self.dt)
        self.SetStatusText('time: '+str(frame_num*self.dt))
        self.im.set_data(self.ldata[frame_num])
        self.axes.draw_artist(self.im)
        self.canvas.blit(self.axes.bbox)

    def OnStop(self,event):
        self.STOP = 'True'

    def OnForward(self,event):
        state =	 self.forward_toggle.GetValue()
        if state:
            self.reverse_play = False
            self.forward_toggle.SetLabel("Forward ")
            self.forward_toggle.SetForegroundColour('black')
            self.forward_toggle.SetBackgroundColour('yellow')
        else:
            self.reverse_play = True
            self.forward_toggle.SetLabel("  Back  ")
            self.forward_toggle.SetForegroundColour('red')
            self.forward_toggle.SetBackgroundColour('green')

    def OnFast(self,event):
        state =	 self.fast_toggle.GetValue()
        if state:
            # print state
            self.fast_toggle.SetLabel(" Normal ")
            self.fast_toggle.SetForegroundColour('black')
            self.fast_toggle.SetBackgroundColour('yellow')
            self.step = 1
        else:
            # print state
            self.fast_toggle.SetLabel("  Fast  ")
            self.fast_toggle.SetForegroundColour('red')
            self.fast_toggle.SetBackgroundColour('green')
            self.step = 10

class MainApp(wx.App):
    def OnInit(self):
       frame = PlotFrame("G-3 Network View 1.7", (50, 60), (480, 640))
       frame.Show()
       self.SetTopWindow(frame)
       return True
# The main loop
                
if __name__ == '__main__':
    app = MainApp(False)
    app.MainLoop()