Any Peak Finder Interactive


Find peak height for a random list of potentiostat data files, interactive version

PeakFinder
Usage Video: Demonstration of Use
Sample Data files: Voltammograms

—————————————

#!/usr/bin/env python

"""This program finds peak height values (i.e., peak currents) from .txt files
and .csv files containing squarewave voltammogram data, using any
selected files. """

__author__ = "Andrew J. Bonham"
__copyright__ = "Copyright 2010, Andrew J. Bonham"
__credits__ = ["Andrew J. Bonham"]
__version__ = 1.20
__maintainer__ = "Andrew J. Bonham"
__email__ = "bonham@gmail.com"
__status__ = "Production"

# Setup: import basic modules we need
import csv
import os
import time
import platform
import glob
import re
import sys

# Setup: import Tkinter for GUI
import Tkinter
try:
    import ttk
except ImportError:
    import Tkinter as ttk
import tkFileDialog

# Setup: import curve fitting and plotting
import scipy
import scipy.optimize
import pylab
import numpy
from scipy.optimize import leastsq

# Define our classes


class PointBrowser:

    '''This is the class that draws the main graph of data for Peak Finder.

    This class creates a line and point graph with clickable points.
    When a point is clicked, it calls the normal test Fit display
    from the main logic.'''

    def __init__(self, app, logic, xticksRaw, y, fileTitle):
        '''Create the main output graph and make it clickable.'''
        self.app = app
        self.logic = logic
        self.x = range(len(xticksRaw))
        self.y = y
        self.xticks = tuple(xticksRaw)
        self.loc = [d + 0.5 for d in self.x]
        self.fig = pylab.figure(1)
        self.ax = self.fig.add_subplot(111)
        self.ax.plot(self.x, self.y, 'bo-', picker=5)
        self.fig.subplots_adjust(left=0.17)
        self.ax.set_xlabel('File')
        self.ax.get_xaxis().set_ticks([])
        self.ax.set_ylabel('Current (A)')
        self.ax.ticklabel_format(style='sci', scilimits=(0, 0), axis='y')
        self.ax.set_title('Peak Current for ' + str(fileTitle))
        pylab.get_current_fig_manager().window.wm_geometry("400x400+400+10")
        pylab.draw()

        self.lastind = 0

        self.selected, = self.ax.plot([self.x[0]], [self.y[0]], 'o', ms=12,
                                      alpha=0.4, color='yellow', visible=False)

        # Display button to fetch data
        self.axb = pylab.axes([0.75, 0.03, 0.15, 0.05])
        self.button = pylab.Button(self.axb, 'Results')
        self.button.on_clicked(self.app.data_popup)

        self.fig.canvas.mpl_connect('pick_event', self.onpick)

        pylab.show()

    def onpick(self, event):
        '''Capture the click event, find the corresponding data
        point, then update accordingly.'''

        # the click locations
        try:
            x = event.mouseevent.xdata
            y = event.mouseevent.ydata

            dx = numpy.array(x - self.x[event.ind], dtype=float)
            dy = numpy.array(y - self.y[event.ind], dtype=float)

            distances = numpy.hypot(dx, dy)
            indmin = distances.argmin()
            dataind = event.ind[indmin]

            self.lastind = dataind
            self.update()
        except Exception:
            pass

    def update(self):
        '''Update the main graph and call my response function.'''
        if self.lastind is None:
            return

        dataind = self.lastind

        self.selected.set_visible(True)
        self.selected.set_data(self.x[dataind], self.y[dataind])

        self.logic.test_fit(dataind)

        self.fig.canvas.draw()


class PeakFinderApp(Tkinter.Tk):

    '''This is the gui for Peak Finder.

    This application displays a minimal user interface to select a
    directory and to specify file formats and output filename, then
    reports on the programs function with a simple progressbar.'''

    def __init__(self):
        '''set up the peak finder app GUI.'''
        self.directory_manager()
        Tkinter.Tk.__init__(self)
        self.window_title = "Any Peak Finder " + str(__version__)

        # invoke PeakLogicFiles to do the actual work
        logic = PeakLogicFiles(self)

        # create the frame for the main window
        self.title(self.window_title)
        self.geometry('+100+100')
        self.resizable(Tkinter.FALSE, Tkinter.FALSE)
        mainframe = ttk.Frame(self)
        mainframe.grid(column=0, row=0, sticky=(
            Tkinter.N, Tkinter.W, Tkinter.E, Tkinter.S), padx=5, pady=5)
        mainframe.columnconfigure(0, weight=1)
        mainframe.rowconfigure(0, weight=1)

        # define our variables in GUI-form and give sane defaults
        self.filename_ = Tkinter.StringVar(value='output1')
        self.output = Tkinter.StringVar()
        self.filenames_ = Tkinter.StringVar()
        self.dir_selected = Tkinter.IntVar(value=0)
        self.init_potential_ = Tkinter.DoubleVar(value=-0.15)
        self.final_potential_ = Tkinter.DoubleVar(value=-0.35)
        self.peak_center_ = Tkinter.DoubleVar(value=-0.25)
        self.final_edge_ = Tkinter.DoubleVar(value=-1)
        self.init_edge_ = Tkinter.DoubleVar(value=1)

        # display the entry boxes
        ttk.Label(
            mainframe, text=self.window_title, font='helvetica 12 bold').grid(
            column=1, row=1, sticky=Tkinter.W, columnspan=2)

        self.filename_entry = ttk.Entry(
            mainframe, width=12, textvariable=self.filename_).grid(
            column=2, row=2, sticky=(Tkinter.W, Tkinter.E))
        ttk.Label(
            mainframe, text="Filename:").grid(
            column=1, row=2, sticky=Tkinter.W)

        self.init_potential = ttk.Entry(
            mainframe, width=12, textvariable=self.init_potential_).grid(
            column=2, row=7, sticky=(Tkinter.W, Tkinter.E))
        ttk.Label(
            mainframe, text="Initial Potential:").grid(
            column=1, row=7, sticky=Tkinter.W)

        self.final_potential = ttk.Entry(
            mainframe, width=12, textvariable=self.final_potential_).grid(
            column=2, row=8, sticky=(Tkinter.W, Tkinter.E))
        ttk.Label(
            mainframe, text="Final Potential:").grid(
            column=1, row=8, sticky=Tkinter.W)

        self.peak_center = ttk.Entry(
            mainframe, width=12, textvariable=self.peak_center_).grid(
            column=2, row=9, sticky=(Tkinter.W, Tkinter.E))
        ttk.Label(
            mainframe, text="Peak Center:").grid(
            column=1, row=9, sticky=Tkinter.W)

        ttk.Label(
            mainframe, text="Peak Location Guess",
            font='helvetica 10 bold').grid(
            column=1, row=5, sticky=Tkinter.W)
        ttk.Label(
            mainframe, text="(only change if necessary)").grid(
            column=1, row=6, sticky=Tkinter.W)

        # Display Generate button
        ttk.Button(
            mainframe, text="Find Peaks", command=logic.peakfinder).grid(
            column=1, row=10, sticky=Tkinter.W)

        # Display test fit button
        ttk.Button(
            mainframe, text="Test fit", command=logic.test_fit).grid(
            column=2, row=10, sticky=Tkinter.W)

        # Display Directory button
        ttk.Button(
            mainframe, text="Select Files", command=self.files_select).grid(
            column=1, row=4, sticky=Tkinter.W)

        # Show the output
        ttk.Label(
            mainframe, textvariable=self.output).grid(
            column=1, row=13, sticky=(Tkinter.W, Tkinter.E), columnspan=5)

        # Set up Outlier correction
        ttk.Label(
            mainframe, text="Regions to include (from -> to):").grid(
            column=1, row=12, sticky=Tkinter.W)
        self.final_edge = ttk.Entry(
            mainframe, width=12, textvariable=self.final_edge_).grid(
            column=1, row=13, sticky=(Tkinter.E))
        self.init_edge = ttk.Entry(
            mainframe, width=12, textvariable=self.init_edge_).grid(
            column=2, row=13, sticky=(Tkinter.W, Tkinter.E))

        # Pad the windows for prettiness
        for child in mainframe.winfo_children():
            child.grid_configure(padx=5, pady=5)

        # Display a progressbar
        self.bar = ProgressBar(mainframe, width=100, height=10)
        self.bar.update(0)

        ttk.Label(mainframe, text="").grid(
            column=1, row=16, sticky=(Tkinter.W, Tkinter.E))

        # add a system menu
        self.option_add('*tearOff', Tkinter.FALSE)

        self.menubar = Tkinter.Menu(self)
        self['menu'] = self.menubar

        self.menu_file = Tkinter.Menu(self.menubar)
        self.menubar.add_cascade(menu=self.menu_file, label='File')

        self.menu_file.add_command(label='Exit', command=self.destroy)

        self.menu_help = Tkinter.Menu(self.menubar, name='help')
        self.menubar.add_cascade(menu=self.menu_help, label='Help')

        self.menu_help.add_command(label='How to Use', command=self.help_popup)
        self.menu_help.add_command(
            label='About Peak Finder...', command=self.about_popup)

        # Run the program
        pylab.show()
        self.mainloop()

    def directory_manager(self):
        '''Set the initial directory to the users home directory.'''
        if platform.system() == 'Windows':
            # import windows file management
            from win32com.shell import shell, shellcon
            self.mydocs = shell.SHGetFolderPath(
                0, shellcon.CSIDL_PERSONAL, 0, 0)
        else:
            # import mac file management
            self.mydocs = os.getenv("HOME")
        os.chdir(self.mydocs)

    def about_popup(self):
        '''Display a pop-up menu about the program.'''
        self.aboutDialog = Tkinter.Toplevel(self)
        self.aboutDialog.resizable(Tkinter.FALSE, Tkinter.FALSE)
        self.aboutDialog.geometry('+400+100')
        # Create the frame for the about window
        aboutframe = ttk.Frame(self.aboutDialog, width='200', height='200')
        aboutframe.grid_propagate(0)
        aboutframe.grid(column=0, row=0, sticky=(
            Tkinter.N, Tkinter.W, Tkinter.E, Tkinter.S), padx=5, pady=5)
        aboutframe.columnconfigure(0, weight=1)
        aboutframe.rowconfigure(0, weight=1)
        ttk.Label(
            aboutframe, text=self.window_title, font='helvetica 12 bold',
            anchor="center").grid(
            column=0, row=0, sticky=(Tkinter.N, Tkinter.W, Tkinter.E))
        ttk.Label(aboutframe, text='Voltammogram data analysis\nsoftware for' +
                  'CH Instruments data.\n\nWritten by\n' + str(__author__) +
                  '\nhttp://www.andrewjbonham.com\n' + str(__copyright__) +
                  '\n\n\n', anchor="center", justify="center").grid(
            column=0, row=1, sticky=Tkinter.N)
        ttk.Button(
            aboutframe, text="Close", command=self.aboutDialog.destroy).grid(
            column=0, row=4, sticky=(Tkinter.S))
        self.aboutDialog.mainloop()

    def help_popup(self):
        '''Display a pop-up menu explaining how to use the program.'''
        self.helpDialog = Tkinter.Toplevel(self)
        self.helpDialog.resizable(Tkinter.FALSE, Tkinter.FALSE)
        self.helpDialog.geometry('+400+100')
        # Create the frame for the help window
        helpframe = ttk.Frame(self.helpDialog, width='200', height='240')
        helpframe.grid(column=0, row=0, sticky=(
            Tkinter.N, Tkinter.W, Tkinter.E, Tkinter.S), padx=5, pady=5)
        helpframe.columnconfigure(0, weight=1)
        helpframe.rowconfigure(0, weight=1)
        ttk.Label(
            helpframe, text=self.window_title + ' Help',
            font='helvetica 12 bold',
            anchor="center").grid(column=0, row=0, sticky=(
            Tkinter.N, Tkinter.W, Tkinter.E))
        helptext = Tkinter.Text(helpframe, width=40, height=9)
        helpmessage = "Peak Finder is used to find the peak current for a" +\
            "methylene blue reduction peak for CH instruments voltammogram" +\
            "data files. The data files must be sequentially numbered" +\
            "in the specified directory and be in text format.\n\n" +\
            "The initial and final potential should be adjusted to lie" +\
            "outside the gaussian peak."
        helptext.insert('1.0', helpmessage)
        helptext.config(state='disabled', bg='SystemButtonFace',
                        borderwidth=0, font='helvetica 10', wrap='word')
        helptext.grid(column=0, row=1, sticky=Tkinter.N, padx=10)
        ttk.Button(
            helpframe, text="Close", command=self.helpDialog.destroy).grid(
                column=0, row=4, sticky=(Tkinter.S))
        self.helpDialog.mainloop()
    def data_popup(self, event):
        '''Display a pop-up window of data.'''
        filename = str(self.filename_.get()) + ".csv"
        self.dataDialog = Tkinter.Toplevel(self)
        self.dataDialog.resizable(Tkinter.FALSE, Tkinter.FALSE)
        self.dataDialog.geometry('+400+100')
        # Create the frame for the data window
        dataframe = ttk.Frame(self.dataDialog, width='300', height='600')
        dataframe.grid_propagate(0)
        dataframe.grid(column=0, row=0, sticky=(
            Tkinter.N, Tkinter.W, Tkinter.E, Tkinter.S), padx=5, pady=5)
        dataframe.columnconfigure(0, weight=1)
        dataframe.rowconfigure(0, weight=1)
        ttk.Label(
            dataframe, text='Data for ' + str(filename),
            font='helvetica 12 bold',
            anchor="center").grid(
            column=0, row=0, sticky=(Tkinter.N, Tkinter.W, Tkinter.E))

        # Read the output data
        self.df = csv.reader(open(filename), delimiter=',')
        listfile = []
        for row in self.df:
            listfile.append(row)
        data_formatter = []
        for item in listfile:
            data_formatter.append(' '.join(map(str, item)))
        data_output = "\n".join(map(str, data_formatter))

        # Display it
        self.datatext = Tkinter.Text(dataframe, width=30, height=33)
        self.datatext.grid(
            column=0, row=1, sticky=(Tkinter.N, Tkinter.W, Tkinter.E))
        self.datatext.insert('1.0', data_output)
        ttk.Button(
            dataframe, text="Close", command=self.dataDialog.destroy).grid(
            column=0, row=4, sticky=(Tkinter.S))
        self.dataDialog.mainloop()

    def files_select(self, *args):
        '''Allow user to select a directory where datafiles
        are located.'''
        try:
            filenamesRaw = tkFileDialog.askopenfilenames(
                title='Title',
                filetypes=[("TXT Files", "*.txt"), ("CSV Files", "*.csv")])
            self.filenames_.set(filenamesRaw)
            self.dir_selected.set(1)
            return longpath
        except Exception:
            pass


class PeakLogicFiles():

    '''This is the internal logic of Peak Finder.

    PeaklogicFiles looks at a user-provided list of files, then fits
    the square wave voltammogram data inside to a non-linear polynomial
    plus gaussian function, subtracts the polynomial, and reports the
    peak current.  Ultimately, it prints a pretty graph of the data.'''

    def __init__(self, app):
        '''Initialize PeakLogic, passing the gui object to this class.'''
        self.app = app

    def peakfinder(self):
        '''PeakLogic.peakfinder is the core function to find and report
        peak current values.'''
        # Make sure the user has selected files
        if int(self.app.dir_selected.get()) == 1:
            try:
                # grab the text variables that we need
                filename = str(self.app.filename_.get())
                filenames = self.app.filenames_.get()
                filenamesList = list(self.app.tk.splitlist(filenames))
                filenamesList.append(filenamesList.pop(0))
                filenamesList = tuple(filenamesList)
                path = os.path.split(filenamesList[0])[0]
                self.app.bar.set_maxval(len(filenamesList) + 1)
                os.chdir(path)

                # open our self.output file and set it up
                with open(str(filename) + ".csv", 'w') as self.g:
                    self.g.write(str(filename) + '\n')
                    self.g.write('\n')
                    self.g.write('Fitting Parameters' + '\n')
                    self.g.write(
                        ",".join(('Init Potential',
                                  str(self.app.init_potential_.get()))) + '\n')
                    self.g.write(
                        ",".join(('Final Potential',
                                  str(self.app.final_potential_.get()))) + '\n')
                    self.g.write(
                        ",".join(('Peak Center',
                                  str(self.app.peak_center_.get()))) + '\n')
                    self.g.write(
                        ",".join(('Left Edge',
                                  str(self.app.init_edge_.get()))) + '\n')
                    self.g.write(
                        ",".join(('Right Edge',
                                  str(self.app.final_edge_.get()))) + '\n')
                    self.g.write('\n')
                    self.g.write(",".join(('--------', '--------')) + '\n')
                    self.g.write(",".join(('time', 'file', 'peak current')) +
                                 '\n')
                    # run the peakfinder
                    printing_list, iplist = self.loop_for_peaks_files(filenamesList)

                # Show the user what was found
                self.app.output.set("Wrote output to " + filename + ".csv")
                mainGraph = PointBrowser(
                    self.app, self, printing_list, iplist, filename)
                return iplist

            except ValueError:
                raise
        else:
            pass

    def loop_for_peaks_files(self, filenamesList):
        '''PeakLogic.loopForPeaks() will loop through each file,
        collecting data and sending it to the peak_math function.'''
        # clear some lists to hold our data
        full_x_lists = []
        full_y_lists = []
        startT = -1
        timelist = []
        realtimelist = []
        printing_list = []
        try:
            pylab.close(2)  # close test fitting graph if open
        except Exception:
            pass
        for each in filenamesList:  # loop through each file
            try:
                dialect = csv.Sniffer().sniff(
                    open(each).read(1024), delimiters="\t,")
                open(each).seek(0)
                self.f = csv.reader(open(each), dialect)
                listfile = []
                for row in self.f:
                    # turn the csv.reader object into a list
                    listfile.append(row)
                t_list = []
                y_list = []
                rx_list = []

                # remove the header rows from the file, leaving just the data
                start_pattern = 3
                for index, line in enumerate(listfile):
                    try:
                        if line[0]:
                            if re.match('Potential*', line[0]):
                                start_pattern = index
                                # print "Found it"
                    except Exception:
                        pass
                datalist = listfile[start_pattern + 2:]
                pointT = 1000
                # if it's the first data point, set the initial time to zero
                if startT == -1:
                    startT = pointT
                # subtract init time to get time since the start of the trial
                pointTcorr = pointT - startT
                t_list.append(pointTcorr)
                for row in datalist:
                    if row == []:  # skip empty lines
                        pass
                    else:
                        if row[0]:
                            rx_list.append(row[0])
                        if row[1]:
                            y_list.append(row[1])
                        else:
                            pass
                full_x_lists.append(rx_list)
                full_y_lists.append(y_list)
                timelist.append(pointTcorr)
                justName = os.path.split(each)[1]
                printing_list.append(justName)
            except Exception:
                pass
        iplist = self.peak_math(full_x_lists, full_y_lists)
        # write the output csv file
        for i, v, y in zip(iplist, timelist, printing_list):
            self.g.write(str(v) + ',' + str(y) + ',' + str(i) + '\n')
        return timelist, iplist  # return time and peak current for graphing

    def peak_math(self, listsx, listsy):
        '''PeakLogic.peak_math() fits the data to a polynomial and a
        gaussian, then subtracts the polynomial to find peak current.'''
        # give reasonable starting values for non-linear regression
        v0 = [1.5e-6, -1e-7, 3e-6, 5e-7, -0.322, 30]
        iplist = []
        count = 1
        for xfile, yfile in zip(listsx, listsy):
            ip = self.fitting_math(xfile, yfile, 1)
            if ip < 0:
                ip = 0
            iplist.append(ip)
            self.app.bar.update(count)
            count = count + 1

        return iplist

    def fitting_math(self, xfile, yfile, flag=1):
        '''PeakLogic.fitting_math() fits the data to a cosh and a
        gaussian, then subtracts the cosh to find peak current..'''
        try:
            init_pot = self.app.init_potential_.get()
            final_pot = self.app.final_potential_.get()
            edgelength = numpy.abs(init_pot - final_pot)
            center = self.app.peak_center_.get()  # - now not used
            AA = []
            x = numpy.array(xfile, dtype=numpy.float64)
            y = numpy.array(yfile, dtype=numpy.float64)
            # fp is full portion with the cosh plus gaussian
            fp = lambda v, x: v[1] * numpy.cosh(v[0] * (x - v[2])) + v[
                3] * numpy.exp(-(((x - v[4]) ** 2) / (2 * v[5] ** 2)))
            # pp is the cosh portion
            pp = lambda v, x: v[1] * numpy.cosh(v[0] * (x - v[2]))
            # gp is the gaussian portion - now not used
            gp = lambda v, x: v[3] * numpy.exp(
                -(((x - v[4]) ** 2) / (2 * v[5] ** 2)))

            # ep = lambda v, x, y: (pp(v,x)-y)  #ep is the error for a cosh fit
            # eg = lambda v, x, y: (gp(v,x)-y)  #eg is the error for a gaussian
            # e is the error of the full fit from the real data
            e = lambda v, x, y: (fp(v, x) - y)

            # First, we will fit the data
            # cut out outliers
            passingx, passingy = self.trunc_edges(xfile, yfile)

            # cut out the middle values and return the edges
            outx, outy = self.trunc_list(passingx, passingy)
            AA = numpy.average(outy)
            less = (passingx < init_pot)
            greater = passingx > final_pot
            try:
                PeakHeight = numpy.max(passingy[less & greater])
            except Exception:
                PeakHeight = numpy.max(passingy)
            center = numpy.max(passingx[passingy == PeakHeight])
            # give reasonable starting values for non-linear regression
            v0 = [0.5, AA, numpy.average(
                passingx), PeakHeight, center, edgelength / 6]
            y_subbed = []  # clear a list for putting revised y values

            # fit the gaussin and baseline to all data
            v, success = leastsq(e, v0, args=(passingx, passingy))
            ip = (fp(v, v[4]) - pp(v, v[4]))
                  # find the max value of the gaussian peak (minus the
                  # polynomial)
            if v[4] > init_pot:
                ip = 0
            if v[4] < final_pot:
                ip = 0
            if flag == 1:
                return ip
            if flag == 0:
                return x, y, fp(v, passingx), pp(v, passingx), ip, passingx
        except Exception:
            print "Error Fitting"
            print sys.exc_info()
            return -1

    def trunc_list(self, listx, listy):
        '''Remove the central portions of an x-y data list (where we
        suspect the gaussian is found).'''
        newx = []
        newy = []
        for spot, h in zip(listx, listy):
            spot = float(spot)  # convert from string
            h = float(h)  # convert from string
            low = float(self.app.final_potential_.get())
            high = float(self.app.init_potential_.get())
            if spot < low:  # add low values
                newx.append(spot)
                newy.append(h)
            else:
                pass
            if spot > high:  # add high values
                newx.append(spot)
                newy.append(h)
            else:
                pass
        px = numpy.array(newx, dtype=numpy.float64)
        py = numpy.array(newy, dtype=numpy.float64)
        return px, py

    def trunc_edges(self, listx, listy):
        '''PeakLogic.trunc_edges() removes outlier regions of known
        bad signal from an x-y data list and returns the inner edges.'''
        newx, newy = [], []  # clear lists
        for spot, h in zip(listx, listy):
            spot = float(spot)  # convert from string
            h = float(h)  # convert from string
            low = float(self.app.final_edge_.get())
            high = float(self.app.init_edge_.get())
            if spot > low:  # add low values
                # print "Ping"
                if spot < high:
                    # print "Pong"
                    newx.append(spot)
                    newy.append(h)
                else:
                    # print "Inner Pass"
                    pass
            else:
                # print "Outer Pass"
                pass
        # convert results back to an array
        px = numpy.array(newx, dtype=numpy.float64)
        py = numpy.array(newy, dtype=numpy.float64)
        return px, py  # return partial x and partial y

    def test_fit(self, dataind=0):
        '''Perform a fit for the first data point and display it for
        the user.'''
        # Make sure the user has selected a directory
        if int(self.app.dir_selected.get()) == 1:
            try:
                filename = str(self.app.filename_.get())
                filenames = self.app.filenames_.get()
                filenamesList = list(self.app.tk.splitlist(filenames))
                filenamesList.append(filenamesList.pop(0))
                filenamesList = tuple(filenamesList)

                file = filenamesList[dataind]
                dialect = csv.Sniffer().sniff(
                    open(file).read(1024), delimiters="\t,")
                open(file).seek(0)
                # open the first data file
                self.testfile = csv.reader(open(file), dialect)
                listfile = []
                for row in self.testfile:
                    # turn the csv.reader object into a list
                    listfile.append(row)

                # remove the header rows from the file, leaving just the data
                start_pattern = 3
                for index, line in enumerate(listfile):
                    try:
                        if line[0]:
                            if re.match('Potential*', line[0]):
                                start_pattern = index
                                # print "Found it"
                    except Exception:
                        pass
                datalist = listfile[start_pattern + 2:]

                x_list = []
                y_list = []
                for row in datalist:
                    if row == []:  # skip empty lines
                        pass
                    else:
                        if row[0]:
                            x_list.append(row[0])
                        if row[1]:
                            y_list.append(row[1])
                        else:
                            pass
                x, y, y_fp, y_pp, ip, px = self.fitting_math(
                    x_list, y_list, flag=0)
                self.test_grapher(x, y, y_fp, y_pp, file, ip, px)
            except Exception:
                pass
        else:
            pass

    def test_grapher(self, x, y, y_fp, y_pp, file, ip, px):
        '''PeakLogic.test_grapher() displays a graph of the test fitting.'''
        try:
            try:
                pylab.close(2)  # close previous test if open
            except Exception:
                pass
            file_name = os.path.basename(file)
            self.fig2 = pylab.figure(2)
            self.ax2 = self.fig2.add_subplot(111)
            self.ax2.plot(x, y, 'ro', label='data')
            self.ax2.plot(px, y_fp, label='fit')
            self.ax2.plot(px, y_pp, label='baseline')
            self.ax2.set_xlabel('Potential (V)')
            self.ax2.set_ylabel('Current (A)')
            self.ax2.set_title("Fit of " + str(file_name))
            self.ax2.ticklabel_format(style='sci', scilimits=(0, 0), axis='y')
            self.ax2.legend()
            self.fig2.subplots_adjust(bottom=0.15)
            self.fig2.subplots_adjust(left=0.15)
            self.text = self.ax2.text(0.05, 0.95, 'Peak Current:\n%.2e A' % ip,
                                      transform=self.ax2.transAxes, va='top')
            pylab.get_current_fig_manager().window.wm_geometry(
                "300x300+400+500")
            pylab.show()

        except Exception:
            pass


class ProgressBar():

    '''Create a Tkinter Progress bar widget.'''

    def __init__(self, root, width=100, height=10, maxval=100):
        '''Initialize ProgressBar to make the Tkinter progressbar.'''
        self.root = root
        self.maxval = float(maxval)
        self.canvas = Tkinter.Canvas(
            self.root, width=width, height=height,
            highlightt=0, relief='ridge', borderwidth=2)
        self.canvas.create_rectangle(0, 0, 0, 0, fill='blue')
        self.label = ttk.Label(self.root, text='Progress:').grid(
            column=1, row=14, sticky=Tkinter.W)
        self.canvas.grid(
            column=1, row=15, sticky=(Tkinter.W, Tkinter.E), columnspan=3)

    def set_maxval(self, maxval):
        '''ProgressBar.set_maxval() sets the max value of the
        progressbar.'''
        self.maxval = float(maxval)

    def update(self, value=0):
        '''ProgressBar.update() updates the progressbar to a specified
        value.'''
        if value < 0:
            value = 0
        elif value > self.maxval:
            value = self.maxval
        self.canvas.delete(Tkinter.ALL)
        self.canvas.create_rectangle(
            0, 0, self.canvas.winfo_width() * value / self.maxval,
            self.canvas.winfo_reqheight(), fill='blue')
        self.root.update()

# Party ####
if __name__ == '__main__':
    app = PeakFinderApp()