# PCA Routine

import xasinput
from spdirvars import *
from spglobalfuncs import *
from Numeric import *
from Tkinter import *
#from Ifeffit import *
import Pmw
import Tix
import tkFileDialog
import tkMessageBox
import tkSimpleDialog
import os
import math
import types
from string import *
import LinearAlgebra
import spline
import varimax

#root=Tix.Tk()
#root.title("SixPack PCA")
#Pmw.initialise(root)
if os.name=='nt':os.sep='/'
filepath=os.getcwd()+os.sep


###############################################################
##
## Main PCA Program Class
##
###############################################################

class pcasampobj:
    def __init__(self):
        self.xdat=[]
        self.ydat=[]
        self.path=''

class pcacompobj:
    def __init__(self):
        self.active=1
        self.eigen=0
        self.var=0
        self.vartot=0
        self.ind=0
        self.xdat=[]
        self.ydat=[]

class pcamenuplot:
    def __init__(self,master,but):
        self.text=but
        self.mast=master

    def __call__(self):
        self.mast.plotclick(self.text)

class PCAMain:
    global lastsavedir,lastreaddir
    def __init__(self,master,root): #(self,master,root):
        self.pcaw=pcaw=master
        #self.pcaw=pcaw=Toplevel(root)
        self.root=root
        #define scrollable frame
        if root.winfo_screenheight()<860:
            self.sf=Pmw.ScrolledFrame(pcaw,usehullsize=1,hull_width=985,hull_height=720)
            self.pca=pca=self.sf.interior()
            self.sf.pack(side=TOP)
        else:
            self.pca=pca=pcaw
        #GUI for data viewer window
        menubar=Pmw.MenuBar(pca)
        #file menu
        menubar.addmenu('File','')
        menubar.addmenuitem('File','command',label='Open Dataset',command=self.loaddatafile)
        menubar.addmenuitem('File','command',label='Open Multiple Datasets',command=self.loadmultdatafile)
        menubar.addmenuitem('File','separator')    
        menubar.addmenuitem('File','command',label='Clear Datasets',command=self.clearfilelist)
        menubar.addmenuitem('File','separator')
        menubar.addmenuitem('File','command',label='Reload Datasets',command=self.reloadallsamps)
        menubar.addmenuitem('File','separator')
        menubar.addmenuitem('File','command',label='Edit Default Directories',command=self.callchangedefdirs)
        menubar.addmenuitem('File','separator')        
        menubar.addmenuitem('File','command',label='Close',command=self.closemod)        
        menubar.addmenuitem('File','command',label='Quit',command=self.root.quit)
        menubar.addmenu('Save','')
        menubar.addmenuitem('Save','command',label='Components',command=self.savePCAcomponents)
        menubar.addmenuitem('Save','command',label='Results',command=DISABLED,state=DISABLED)
        menubar.addmenu('Interpolate','')
        self.nointerp=IntVar()
        menubar.addmenuitem('Interpolate','checkbutton',label='Turn Off Interpolation',variable=self.nointerp)        
        menubar.addmenu('Plot','')
        menubar.addmenuitem('Plot','command',label='Scree Plots',command=pcamenuplot(self,'Scree'))
        menubar.addmenuitem('Plot','command',label='Variance Plots',command=pcamenuplot(self,'Variance'))
        menubar.addmenuitem('Plot','separator')    
        menubar.addmenuitem('Plot','command',label='Plot Component',command=pcamenuplot(self,'Component'))
        menubar.addmenuitem('Plot','command',label='Plot All Components',command=pcamenuplot(self,'All Components'))
        menubar.addmenuitem('Plot','command',label='Plot Reconstruction/Sample Pair',command=pcamenuplot(self,'Reconstruction/Sample Pair'))
        menubar.addmenuitem('Plot','command',label='Plot Sample',command=pcamenuplot(self,'Sample'))
        menubar.addmenuitem('Plot','command',label='Plot All Samples',command=pcamenuplot(self,'All Samples'))
        menubar.addmenuitem('Plot','separator') 
        menubar.addmenuitem('Plot','command',label='Plot Target Transform',command=pcamenuplot(self,'Target Transform'))
        menubar.addmenuitem('Plot','separator') 
        menubar.addmenuitem('Plot','command',label='Rescale Plot',command=self.rescaleplot)
        menubar.addmenuitem('Plot','separator') 
        menubar.addmenuitem('Plot','command',label='Copy Plot Data to Clipboard',command=self.graphtextport) 
        menubar.addmenu('Rotation','')
        menubar.addmenuitem('Rotation','command',label='Do Varimax on vectors',command=self.dovarimaxb)        
        menubar.addmenuitem('Rotation','command',label='Do Varimax on scaled vectors',command=self.dovarimaxbd)
        menubar.addmenu('Library','')
        menubar.addmenuitem('Library','command',label='Create File',command=self.createlibrary) 
        menubar.addmenuitem('Library','command',label='Run Analysis on Library',command=self.targetlibrary) 
        menubar.addmenu('Help','',side=RIGHT)
        menubar.addmenuitem('Help','command',label='About SixPack',command=self.callprogramabout)
        menubar.grid(row=0,columnspan=6,sticky=W+E)
        #Add project manager window
        pmwin=Frame(pca,relief=SUNKEN,bd=2)
        pmwin.grid(row=1,column=0,columnspan=1,rowspan=15,sticky=N+S+E+W)
        l=Label(pmwin,text='Sample Files',relief=RAISED,bd=2)
        l.pack(side=TOP,fill=X,padx=2,pady=4)
        act=Frame(pmwin)
        w=15
        b=Button(act,text="Add File",width=w,command=self.loaddatafile,bg='steel blue',fg='white')
        b.pack(side=LEFT,padx=2,pady=2)
        b=Button(act,text="Add Many Files",width=w,command=self.loadmultdatafile,bg='darkblue',fg='white')
        b.pack(side=LEFT,padx=2,pady=2)
        act.pack(side=TOP,padx=2)
        act=Frame(pmwin)
        b=Button(act,text="Remove File",width=w,command=self.removefile,bg='red',fg='white')
        b.pack(side=LEFT,padx=2,pady=2)
        b=Button(act,text="Clear All",width=w,command=self.clearfilelist,bg='brown',fg='white')
        b.pack(side=LEFT,padx=2,pady=2)    
        act.pack(side=TOP,padx=2)
        #Add btree
        tree=Tix.ScrolledHList(pmwin)
        self.samlist=tree.hlist
        self.samlist.config(separator='#',width=10, drawbranch=0,indent=0, itemtype=Tix.TEXT, browsecmd=self.selectsamp)
        tree.pack(side=TOP,fill=BOTH,expand=1,padx=2,pady=2)
        #Add graph window
        grwin=Frame(pca,relief=SUNKEN,bd=2)
        grwin.grid(row=1,column=1,columnspan=5,rowspan=15,sticky=W+E+N+S)
        self.graph=Pmw.Blt.Graph(grwin,plotbackground='white')
        self.graph.bind(sequence="<ButtonPress>",   func=self.mouseDown)
        self.graph.bind(sequence="<ButtonRelease>", func=self.mouseUp  )
        self.graph.bind(sequence="<Motion>", func=self.coordreport)
        self.graph.pack(expand=1,fill='both')
        #add component results
        crwin=Frame(pca,relief=SUNKEN,bd=2)
        crwin.grid(row=16,column=0,columnspan=2,rowspan=15,sticky=W+E+N+S)
        l=Label(crwin,text='PCA Control',relief=RAISED,bd=2)
        l.pack(side=TOP,fill=X,padx=2,pady=4)
        b=Button(crwin,text="Do PCA Run",command=self.dopca,bg='darkgreen',fg='snow')
        b.pack(side=TOP,fill=X,padx=2,pady=2)
        #add btree legend
        l=Label(crwin,text='Comp.            Eigen          Var.         Cum Var.         IND  ',anchor=W)
        l.pack(side=TOP,padx=2,pady=2)
        #add btree internals to components
        rtree=Tix.ScrolledHList(crwin,options='hlist.columns 6')
        self.complist=rtree.hlist
        self.complist.config(separator='#',width=40, drawbranch=0, indent=0, itemtype=Tix.TEXT,
                           command=self.choosecomp,browsecmd=DISABLED)
        self.complist.column_width(5,chars=10)
        self.complist.column_width(4,chars=10)
        self.complist.column_width(3,chars=10)
        self.complist.column_width(2,chars=10)
        self.complist.column_width(1,chars=10)
        self.complist.column_width(0,chars=3)
        rtree.pack(side=TOP,fill=BOTH,expand=1,padx=2,pady=2)
        #add plot control
        plotwin=Frame(pca,relief=SUNKEN,bd=2)
        plotwin.grid(row=16,column=2,columnspan=1,rowspan=10,sticky=N+S+E+W)
        l=Label(plotwin,text='Plot Control',relief=RAISED,bd=2)
        l.grid(columnspan=2,sticky=E+W,pady=4)
        w=15
        b=Button(plotwin,text="Scree",width=w,bg='indianred',fg='white')
        b.grid(row=1,column=0,columnspan=1,padx=5,pady=2,sticky=E+W)
        b.bind("<Button-1>",self.plotclick)
        b=Button(plotwin,text="Variance",width=w,bg='firebrick4',fg='white')
        b.grid(row=1,column=1,columnspan=1,padx=5,pady=2,sticky=E+W)
        b.bind("<Button-1>",self.plotclick)
        b=Button(plotwin,text="Component",width=w,bg='chocolate3',fg='white')
        b.grid(row=2,column=0,columnspan=1,padx=5,pady=2,sticky=E+W)
        b.bind("<Button-1>",self.plotclick)
        b=Button(plotwin,text="All Components",width=w,bg='darkorange',fg='white')
        b.grid(row=2,column=1,columnspan=1,padx=5,pady=2,sticky=E+W)
        b.bind("<Button-1>",self.plotclick)
        b=Button(plotwin,text="Sample",width=w,bg='palegreen4',fg='white')
        b.grid(row=3,column=0,columnspan=1,padx=5,pady=2,sticky=E+W)
        b.bind("<Button-1>",self.plotclick)
        b=Button(plotwin,text="All Samples",width=w,bg='darkgreen',fg='white')
        b.grid(row=3,column=1,columnspan=1,padx=5,pady=2,sticky=E+W)
        b.bind("<Button-1>",self.plotclick)
        b=Button(plotwin,text="Reconstruction/Sample Pair",width=2*w+3,bg='royalblue3',fg='white')
        b.grid(row=4,column=0,columnspan=2,padx=5,pady=2,sticky=E+W)
        b.bind("<Button-1>",self.plotclick)
        b=Button(plotwin,text="Target Transform",width=2*w+3,bg='midnightblue',fg='white')
        b.grid(row=5,column=0,columnspan=2,padx=5,pady=2,sticky=E+W)
        b.bind("<Button-1>",self.plotclick)
        #file control
        fitpwin=Frame(pca,relief=SUNKEN,bd=2)
        fitpwin.grid(row=16,column=3,columnspan=1,rowspan=10,sticky=W+E+N+S)
        l=Label(fitpwin,text='File Parameters',relief=RAISED,bd=2)
        l.pack(side=TOP,fill=X,padx=2,pady=4)
        self.xmin=Pmw.EntryField(fitpwin,label_text="xmin:",labelpos=W,validate='real',entry_width=7,value='')
        self.xmax=Pmw.EntryField(fitpwin,label_text="xmax:",labelpos=W,validate='real',entry_width=7,value='')
        self.xwt=Pmw.EntryField(fitpwin,label_text="x-weight:",labelpos=W,validate='real',entry_width=7,value='0')
        self.xmin.pack(fill=X,expand=1,padx=15,pady=3)
        self.xmax.pack(fill=X,expand=1,padx=15,pady=3)
        self.xwt.pack(fill=X,expand=1,padx=15,pady=3)
        Pmw.alignlabels([self.xmin,self.xmax,self.xwt])
        b=Button(fitpwin,text="Reload All",bg='goldenrod',fg='white',command=self.reloadallsamps)
        b.pack(side=TOP,fill=X,padx=5,pady=5)        
        #add target transform control
        tarwin=Frame(pca,relief=SUNKEN,bd=2)
        tarwin.grid(row=16,column=4,columnspan=2,rowspan=10,sticky=W+E+N+S)
        l=Label(tarwin,text='Target Transform',relief=RAISED,bd=2)
        l.pack(side=TOP,fill=X,padx=2,pady=4)
        self.targetfile=Tix.FileEntry(tarwin,label="Target File: ",dialogtype="tk_getOpenFile")
        self.targetfile.pack(side=TOP,padx=2,pady=7)
        b=Button(tarwin,text="Do Target Transform",command=self.dotargettrans,bg='red',fg='white')
        b.pack(side=TOP,fill=X,padx=15,pady=10)    
        self.chisq=Label(tarwin,text='Chi Sq:          ',anchor=W)
        self.chisq.pack(side=TOP,fill=X,padx=5,pady=7)
        self.rval=Label(tarwin,text='Rvalue:          ',anchor=W)
        self.rval.pack(side=TOP,fill=X,padx=5,pady=7)
        self.spoil=Label(tarwin,text='SPOIL:          ',anchor=W)
        self.spoil.pack(side=TOP,fill=X,padx=5,pady=7)
        #add results text box
        resbox=Frame(pca,relief=SUNKEN,bd=2)        
        resbox.grid(row=26,column=2,columnspan=4,rowspan=5,sticky=W+E+N+S)
        self.restext=Pmw.ScrolledText(resbox,hscrollmode='static',vscrollmode='static',label_text='PCA Results',
                                      columnheader=1,rowheader=1,rowcolumnheader=1,rowheader_width=4,rowcolumnheader_width=3,
                                      usehullsize=1,hull_width=650,hull_height=200,text_wrap='none',Header_font="Courier 9",
                                      text_height=10,labelpos=N,borderframe=1,text_font="Courier 9") 
        self.restext.component('rowcolumnheader').insert('end', 'Comp')
        self.restext.configure(text_state = 'disabled',Header_state = 'disabled')
        self.restext.pack(padx=2,pady=0)
        #add status bar
        d=Label(pca,text=" ",width=15)
        d.grid(row=31,column=1,sticky=W+E)
        self.status=Label(pca,text="",bd=2,relief=RAISED,anchor=W,fg='blue')
        self.status.grid(row=31,columnspan=4,sticky=W+E)
        setstatus(self.status,"Ready")
        self.xcoord=Label(pca,text="X=        ",bd=2,width=15,relief=RIDGE,anchor=W,fg='red')
        self.ycoord=Label(pca,text="Y=        ",bd=2,width=15,relief=RIDGE,anchor=W,fg='red')
        self.xcoord.grid(row=31,column=4,columnspan=1,sticky=W+E)
        self.ycoord.grid(row=31,column=5,columnspan=1,sticky=W+E)        
        #init vars
        self.plotstk=0
        self.samfiles=[]
        self.samvars={}
        self.compfiles=[]
        self.compvars={}
        self.prop=[]
        self.targetdat=pcasampobj()
        self.ttransdat=pcasampobj()
        self.zoomstack=[]

    def callprogramabout(self):
        programabout(self.root)

    def callchangedefdirs(self):
        global lastsavedir,lastreaddir
        s=changedefdirs(self.pca)
        lastreaddir=s.newdir
        lastsavedir=s.newdir
        
    def closemod(self):
        self.root.deiconify()
        self.pca.destroy()

    def rescaleplot(self):
        #get last off stack
        if self.zoomstack==[]:
            return
        limit=self.zoomstack.pop()
        self.graph.xaxis_configure(min=limit[0],max=limit[1])
        self.graph.yaxis_configure(min=limit[2],max=limit[3])

    def coordreport(self,event):
        (x,y)=self.graph.invtransform(event.x,event.y)
        xtext="X="+str(x)
        ytext="Y="+str(y)
        xtext=xtext[:12]
        ytext=ytext[:12]
        setstatus(self.xcoord,xtext)
        setstatus(self.ycoord,ytext)
    
    def zoom(self, x0, y0, x1, y1):
        #add last to zoomstack
        a0=self.graph.xaxis_cget("min")
        a1=self.graph.xaxis_cget("max")
        b0=self.graph.yaxis_cget("min")
        b1=self.graph.yaxis_cget("max")        
        self.zoomstack.append((a0,a1,b0,b1))
        #configure
        self.graph.xaxis_configure(min=x0, max=x1)
        self.graph.yaxis_configure(min=y0, max=y1)

    def mouseDrag(self,event):
        global x0, y0, x1, y1, druged
        druged = 1
        (x1, y1) = self.graph.invtransform(event.x, event.y)             
        self.graph.marker_configure("marking rectangle", 
            coords = (x0, y0, x1, y0, x1, y1, x0, y1, x0, y0))
        self.coordreport(event)
    
    def mouseUp(self,event):
        global dragging, druged
        global x0, y0, x1, y1        
        if dragging:
            self.graph.unbind(sequence="<Motion>")
            self.graph.bind(sequence="<Motion>", func=self.coordreport)
            self.graph.marker_delete("marking rectangle")           
            if event.num==1 and druged:
                if x0 <> x1 and y0 <> y1:   
                    # make sure the coordinates are sorted
                    if x0 > x1: x0, x1 = x1, x0
                    if y0 > y1: y0, y1 = y1, y0         
                    self.zoom(x0, y0, x1, y1) # zoom in
            else:
                self.rescaleplot() # zoom out
                           
    def mouseDown(self,event):
        global dragging, x0, y0, druged
        dragging = 0
        druged = 0
        if self.graph.inside(event.x, event.y):
            dragging = 1
            (x0, y0) = self.graph.invtransform(event.x, event.y)
            self.graph.marker_create("line", name="marking rectangle", dashes=(2, 2))
            self.graph.bind(sequence="<Motion>",  func=self.mouseDrag)        

    def loaddatafile(self):
        global lastsavedir,lastreaddir
        if self.xmin.get()=='' or self.xmax.get()=='':
            tkMessageBox.showwarning(title="Parameter Error",message="Cannot load file\nPlease fill in missing x-limit entries",parent=self.pca)
            return            
        infile=ask_for_file([("chi files","*.chi"),("mu files","*.mu"),("all files","*")],lastreaddir)
        if infile !='':
            indir=rfind(infile,os.sep)
            lastreaddir=infile[:indir]
            #get file data
            self.dataread(infile)
            
    def loadmultdatafile(self):
        global lastsavedir,lastreaddir
        if self.xmin.get()=='' or self.xmax.get()=='':
            tkMessageBox.showwarning(title="Parameter Error",message="Cannot load file\nPlease fill in missing x-limit entries",parent=self.pca)
            return
        self.multifd=Tix.ExFileSelectDialog(self.root)
        if lastreaddir!='':
            lastreaddir=replace(lastreaddir,'/','\\')            
            self.multifd.fsbox.configure(directory=lastreaddir)
        self.multifd.fsbox.filelist.listbox.configure(selectmode=MULTIPLE)
        #setup ok button...
        self.multifd.fsbox.ok.configure(command=self.getmultilist)        
        self.multifd.popup()

    def getmultilist(self):
        global lastsavedir,lastreaddir
        multfn=self.multifd.fsbox.filelist.listbox.curselection()
        multdlist=self.multifd.fsbox.filelist.listbox.get(0,END)
        curdir=self.multifd.fsbox.dir.entry.get()
        lastreaddir=curdir
        self.multifd.popdown()
        if multfn !=():
            for sel in multfn:
                curfil=multdlist[int(sel)]
                fulpath=curdir+os.sep+curfil
                #get file data
                self.dataread(fulpath)

    def reloadallsamps(self):
        #store path list in temp variable
        tpath=[]
        for np in self.samfiles:
            #select new sample
            dat=self.samvars.get(np)
            tpath.append(dat.path)
        #reload target if needed
        tp=0
        if self.targetdat.path != '':
            self.targetread()
            tartemp=self.targetdat.path
            tp=1
        #kill all data
        self.clearfilelist()
        #reload files
        for fn in tpath:
            self.dataread(fn)
        if tp==1:
            self.targetfile.entry.insert(END,tartemp)

    def dataread(self,fn):
        comchars=['#','!','%','*']
        #initialize info widget
        wid=pcasampobj()
        #read file
        fid=open(fn,"r")
        lines=fid.read().split('\n')
        for line in lines:
            if line !='' and line[0] not in comchars:
                dr=replace(line,',',' ')
                parsed=split(dr)
                if atof(self.xmin.get())<=atof(parsed[0]) and atof(parsed[0])<=atof(self.xmax.get()):
                    wid.xdat.append(atof(parsed[0]))
                    wid.ydat.append(atof(parsed[1]))
        fid.close()
        wid.path=fn
        #turn into arrays
        wid.xdat=array(wid.xdat)
        wid.ydat=array(wid.ydat)
        #interpolate?  0.1 for XANES, 0.05 for EXAFS
        if self.nointerp.get()==0:
            if atof(self.xmin.get())<1000:
                newstep=0.05 #EXAFS
            else:
                newstep=0.1  #XANES
            try:
                interp=spline.Spline(wid.xdat,wid.ydat)
            except:
                tkMessageBox.showwarning(title="Parameter Error",message="x-limits of of data range",parent=self.pca)
                return
            wid.xdat=arange(atof(self.xmin.get()),atof(self.xmax.get()),newstep)
            wid.ydat=interp(wid.xdat)        
        #apply k-weighting? 
        temp=wid.ydat*(wid.xdat**int(self.xwt.get()))
        wid.ydat=temp
        #append lists/update tree            
        treefile=trimdir(fn)        
        self.samfiles.append(treefile)
        self.samvars.update({treefile:wid})
        self.samlist.add(treefile,text=treefile,state=NORMAL)
        self.samlist.selection_clear()
        self.samlist.selection_set(treefile)
        #update status bar
        self.selectsamp()

    def removefile(self):
        cur=self.samlist.info_selection()
        if cur != ():
            cur=cur[0]            
            #remove all associated vars
            ind=self.samfiles.index(cur)
            self.samfiles.remove(cur)
            self.samlist.delete_entry(cur)
            del self.samvars[cur]
            #reselect
            if len(self.samfiles)==0:
                self.complist.delete_all()
            elif ind != 0:
                new=self.samfiles[ind-1]
                self.samlist.selection_set(new)
            else:
                new=self.samfiles[0]
                self.samlist.selection_set(new)

    def clearfilelist(self):
        #reset all
        self.samfiles=[]
        self.samvars={}
        self.compfiles=[]
        self.compvars={}
        self.samlist.delete_all()
        self.complist.delete_all()
        self.prop=[]
        self.targetdat=pcasampobj()
        self.ttransdat=pcasampobj()
        #clear graph
        glist=self.graph.element_names()
        if glist != ():
            for g in glist:
                self.graph.element_delete(g)
        #clear results
        self.restext.configure(text_state = 'normal',Header_state = 'normal')
        self.restext.clear()
        self.restext.component('columnheader').delete(1.0,END)
        self.restext.component('rowheader').delete(1.0,END)
        self.restext.configure(Header_state = 'disabled')
        self.targetfile.entry.delete(0,END)

    def selectsamp(self, *arg):
        #get data
        cur=self.samlist.info_selection()[0]
        dat=self.samvars.get(cur)
        #update status
        npts=len(dat.xdat)
        setstatus(self.status,"File "+cur+" contains "+str(npts)+" data points")
        
    def choosecomp(self, *arg):
        cur=arg[0]
        wid=self.compvars.get(cur)
        if wid.active==0:
            if cur=='Comp 1':
                wid.active=0.5
                self.updatevariance()
                return
            #turn on
            wid.active=1
            self.complist.item_configure(cur,0,text='X')
        elif wid.active==0.5:
            #turn on
            wid.active=1
            self.complist.item_configure(cur,0,text='X')
        else:
            #turn off
            wid.active=0
            self.complist.item_configure(cur,0,text=' ')
        self.updatevariance()

    def plotclick(self,event):
        #determine plot type
        if type(event)==types.StringType:
            pt=event
        else:
            pt=event.widget.cget('text')
        #define data based on type and plot
        if pt=='Scree':
            self.plotstk=0
            xd=[]
            yd=[]
            i=0
            for np in self.compfiles:
                i=i+1
                dat=self.compvars.get(np)
                if dat.active!=0.5:
                    xd.append(i)
                    yd.append(dat.eigen)
            #make plot
            self.makeplot(tuple(xd),tuple(yd),'Eigens','Scree Plot','c')            
        if pt=='Variance':
            self.plotstk=0
            xd=[0]
            yd=[0]
            i=0
            for np in self.compfiles:
                i=i+1
                dat=self.compvars.get(np)
                if dat.active != 0.5:
                    xd.append(i)
                    yd.append(dat.vartot)
            #make plot
            self.makeplot(tuple(xd),tuple(yd),'Var','Variance Plot','c')
        if pt=='Component':
            self.plotstk=0
            cur=self.complist.info_selection()
            if cur != ():
                cur=cur[0]
                dat=self.compvars.get(cur)
                xd=tuple(dat.xdat)
                yd=tuple(dat.ydat)
                #make plot
                self.makeplot(xd,yd,cur,'Component Plot','l')            
        if pt=='All Components':
            self.plotstk=0
            for np in self.compfiles:
                #select new sample
                dat=self.compvars.get(np)
                if dat.active==1:
                    xd=tuple(dat.xdat)
                    yd=tuple(dat.ydat)
                    #make plot
                    self.makeplot(xd,yd,np,pt,'l')
                    self.plotstk=self.plotstk+1
        if pt=='Sample':
            self.plotstk=0
            #get data
            cur=self.samlist.info_selection()
            if cur != ():
                cur=cur[0]
                dat=self.samvars.get(cur)
                xd=tuple(dat.xdat)
                yd=tuple(dat.ydat)
                #make plot
                self.makeplot(xd,yd,cur,'Sample Plot','l')            
        if pt=='All Samples':
            self.plotstk=0
            for np in self.samfiles:
                #select new sample
                dat=self.samvars.get(np)
                xd=tuple(dat.xdat)
                yd=tuple(dat.ydat)
                #make plot
                self.makeplot(xd,yd,np,pt,'l')
                self.plotstk=self.plotstk+1
        if pt=='Reconstruction/Sample Pair':
            self.plotstk=0
            #get data
            cur=self.samlist.info_selection()
            if self.compfiles==[]:
                return
            if cur != ():
                cur=cur[0]
                #first calculate reconstruction
                #make prop and eigenvector matrices
                rows=len(self.compvars[self.compfiles[0]].xdat)
                cols=len(self.compfiles)    
                ueig=zeros((cols,rows),Float)
                i=0
                for np in self.compfiles:
                    dat=self.compvars.get(np)
                    if dat.active==1:
                        #use this one
                        newe=dat.ydat
                        a=i*rows
                        b=a+rows
                        put(ueig,range(a,b),newe)
                        i=i+1
                ueig=transpose(ueig)
                newmat=matrixmultiply(ueig,self.prop)
                #take slice for actual data to compare...
                ind=self.samfiles.index(cur)
                ytemp=take(newmat,(ind,),1)
                ytemp=transpose(ytemp)
                ytemp=ytemp[0]
                yd=tuple(ytemp)
                xd=tuple(dat.xdat)
                self.makeplot(xd,yd,'Recon.','Pair Plot','l')                 
                self.plotstk=1                
                #then plot sample
                dat=self.samvars.get(cur)
                xd=tuple(dat.xdat)
                yd=tuple(dat.ydat)
                #make plot
                self.makeplot(xd,yd,cur,'Pair Plot','l')                 
        if pt=='Target Transform':
            self.plotstk=0
            #plot target
            xd=tuple(self.targetdat.xdat)
            yd=tuple(self.targetdat.ydat)
            #make plot
            self.makeplot(xd,yd,'target','Target Transform','l')
            self.plotstk=1
            #plot transform
            xd=tuple(self.ttransdat.xdat)
            yd=tuple(self.ttransdat.ydat)
            #make plot
            self.makeplot(xd,yd,'transform','Target Transform','l')            

    def makeplot(self,xd,yd,lt,tt,ltyp):
        #update graph
        #first remove current plot(s) if not stacking
        if self.plotstk==0:
            glist=self.graph.element_names()
            if glist != ():
                for g in glist:
                    self.graph.element_delete(g)
        #def colors
        colors=['blue','red','midnightblue','darkgreen','firebrick4','steel blue','orange','purple4']
        if self.plotstk==0:
            pc='blue'
        else:
            pc=colors[int(fmod(self.plotstk,len(colors)))]
        if ltyp=='l':
            sym=''
        else:
            sym='circle'
        #make plot
        try:
            self.graph.line_create(lt,xdata=xd,ydata=yd,symbol=sym,color=pc)
        except:
            lt=str(self.plotstk)+lt
            self.graph.line_create(lt,xdata=xd,ydata=yd,symbol=sym,color=pc)
        self.graph.configure(title=tt)

    def graphtextport(self):
        #export all data in tab delimited text to clipboard
        text=''
        datay=[]
        datax=[]
        allnames=self.graph.element_names()
        print allnames
        for name in allnames:
            temp=self.graph.element_configure(name,'xdata')
            datax.append(temp[4])
            temp=self.graph.element_configure(name,'ydata')
            datay.append(temp[4])
            text=text+name+'\t\t\t'
        text=text+'\n'
        #parse list now
        pdatax=[]
        pdatay=[]
        alllen=[]
        maxlen=0
        for i in range(len(datax)):
            temp=datax[i]
            pdatax.append(split(temp))
            temp=datay[i]
            temp2=split(temp)
            if maxlen<len(temp2):maxlen=len(temp2)
            alllen.append(len(temp2))
            pdatay.append(temp2)
        #setup text
        for j in range(maxlen):
            for i in range(len(pdatax)):
                if j<alllen[i]:
                    text=text+pdatax[i][j]+'\t'+pdatay[i][j]+'\t\t'
                else:
                    text=text+'\t\t\t'
            text=text+'\n'
        #export to clipboard
        self.root.clipboard_clear()
        self.root.clipboard_append(text)
        setstatus(self.status,"Graph data copied to clipboard")
        
    def dopca(self):
        #check arrays
        nscans=len(self.samfiles)
        if nscans==0:
            return
        #check lengths for each file 
        lenlist=[]
        for d in self.samfiles:
            lenlist.append(len(self.samvars[d].xdat))
        match=1
        for m in lenlist:
            if m != lenlist[0]:match=0
        if match==0:
            #print warning and exit
            mtext="Files not of equal length \nNo action taken \n"
            tkMessageBox.showwarning(title="Averaging Error",message=mtext,parent=self.pca)
            return
        #make matrix
        rows=lenlist[0]
        cols=nscans
        datmat=zeros((cols,rows),Float) #this puts rows in columns
        i=0
        for d in self.samfiles:
            yd=self.samvars[d].ydat
            a=i*rows
            b=a+rows
            i=i+1
            put(datmat,range(a,b),yd)
        #reshape datmat
        datmat=transpose(datmat)
        #do svd and eigens for pca
        (evect,eval,weight)=LinearAlgebra.singular_value_decomposition(datmat)
        evalmat=identity(len(eval))*eval
        #prop=matrixmultiply(evalmat,weight)
        prop=weight
        self.uevect=evect
        self.eval=eval
        evect=matrixmultiply(evect,evalmat)
        self.prop=prop
        self.evect=evect
        esum=sum(eval)
        ecsum=[]
        rateind=[]
        evalsq=eval*eval
        for i in range(len(eval)):
            ecsum.append(sum(eval[0:i+1]))
            temp=sum(evalsq[i+1:len(eval)])
            try:
                div=(len(eval)-(i+1))**5
            except:
                div=0
            if div!=0:
                rateind.append(sqrt(temp/div))
            else:
                rateind.append(0)
        rateind=array(rateind)
        ecsum=array(ecsum)
        varcomp=eval/esum
        varexp=ecsum/esum
        #clear old results
        self.compfiles=[]
        self.compvars={}
        self.complist.delete_all()
        #give results in list window
        evect=transpose(evect) #put eigenvectors back to rows
        for c in range(cols):
            #add component...
            wid=pcacompobj()
            xtemp=tuple(self.samvars[self.samfiles[0]].xdat)
            wid.xdat=array(xtemp)
            ytemp=take(evect,(c,)) #slice of eigevector
            ytup=tuple(ytemp[0])
            wid.ydat=array(ytup)
            wid.eigen=eval[c]
            wid.var=varcomp[c]
            wid.vartot=varexp[c]
            wid.ind=rateind[c]
            rn=str(c+1)
            name='Comp '+rn
            self.compfiles.append(name)
            self.compvars.update({name:wid})
            self.complist.add(name,text=" ",state=NORMAL)
            self.complist.item_create(name,1,text=name)
            self.complist.item_create(name,2,text=valueclip_d(wid.eigen,4))
            self.complist.item_create(name,3,text=valueclip_d(wid.var,4))
            self.complist.item_create(name,4,text=valueclip_d(wid.vartot,4))
            if wid.ind !=0:
                self.complist.item_create(name,5,text=valueclip_d(wid.ind,6))
            else:
                self.complist.item_create(name,5,text=' NA ')
            self.complist.selection_clear()
            #make active
            self.complist.item_configure(name,0,text='X')
        #print results in textbox
        self.restext.configure(text_state = 'normal',Header_state = 'normal')
        #print row headers-comp #
        rowheadtext=''
        for c in range(cols):
            rowheadtext=rowheadtext+center(str(c+1),4)+'\n'
        self.restext.component('rowheader').delete(1.0,END)
        self.restext.component('rowheader').insert('end',rowheadtext)
        #print column headers-samp name
        colheadtext=''
        colwidth=12
        for c in self.samfiles:
            #strip of extension
            ct=trimdirext(c)
            #take last colwidth-2 chars
            cf=ct[-(colwidth-2):len(ct)]
            colheadtext=colheadtext+center(cf,colwidth)
        self.restext.component('columnheader').delete(1.0,END)
        self.restext.component('columnheader').insert('end',colheadtext)
        #print square prop matrix
        self.restext.clear()
        for c in range(cols):
            newline=''
            for i in range(cols):
                vect=self.prop[c,i]
                val=valueclip_d(vect,4)
                val=center(val,colwidth)
                newline=newline+val
            self.restext.insert('end',newline+'\n')            
        self.restext.configure(Header_state = 'disabled')

    def dovarimaxb(self):
        self.dovarimax(0)

    def dovarimaxbd(self):
        self.dovarimax(1)        

    def dovarimax(self,scale):
        #check arrays
        nscans=len(self.samfiles)
        if nscans==0:
            return        
        #make sure pca is run
        #do pca if it hasn't been done yet
        if self.compfiles==[]:
            self.dopca()
        #unselect last component if all are checked... and get active components
        i=0
        ii=0
        ind=[]
        for np in self.compfiles:
            dat=self.compvars.get(np)
            if dat.active==1:
                i=i+1
                ind.append(ii)
            ii=ii+1
        if i==len(self.compfiles):
            dat.active=0
            self.complist.item_configure(np,0,text=' ')
            self.updatevariance()
            ind.pop()
        #re-run pca to make sure we have clean slate...
        self.dopca()
        #now mark componenets selected
        i=0
        for np in self.compfiles:
            dat=self.compvars.get(np)
            if i not in ind:
                dat.active=0
                self.complist.item_configure(np,0,text=' ')
            i=i+1
        #make new uevect matrix
        if scale==0:
            newevect=take(self.uevect,ind,1)
        else:
            newevect=take(self.evect,ind,1)
        (a,b)=varimax.varimax(newevect.copy())
        #make new comp matrix
        rotevect=b
        trevect=transpose(rotevect)
        #put these back in the data columns... (ugh)
        i=0
        for np in self.compfiles:
            dat=self.compvars.get(np)
            if dat.active==1:
                #replace
                ytemp=take(trevect,(i,)) #slice of eigevector
                ytup=tuple(ytemp[0])
                dat.ydat=array(ytup)
            i=i+1

        #rotate proportions?
        tprop=transpose(self.prop)
        nprop=take(tprop,ind,1)
        if scale==0:
            neweval=take(self.eval,ind)
            newevalmat=identity(len(neweval))*neweval
            first=matrixmultiply(transpose(a),newevalmat)
            rtprop=matrixmultiply(first,transpose(nprop))
        if scale==1:
            rtprop=matrixmultiply(transpose(a),transpose(nprop))
        #rtprop=transpose(rprop)
        #make the rest of the props zeros
        newprop=zeros(self.prop.shape,Float32)
        ii=0
        l=newprop.shape[0]
        for i in ind:
            #replace rows
            put(newprop,range(i*l,(i+1)*l),rtprop[ii,:])
            ii=ii+1
        self.prop=newprop.copy()

        #report new props in results...?
        cols=len(self.compfiles)
        #print results in textbox
        self.restext.configure(text_state = 'normal',Header_state = 'normal')
        #print row headers-comp #
        rowheadtext=''
        for c in range(cols):
            rowheadtext=rowheadtext+center(str(c+1),4)+'\n'
        self.restext.component('rowheader').delete(1.0,END)
        self.restext.component('rowheader').insert('end',rowheadtext)
        #print column headers-samp name
        colheadtext=''
        colwidth=12
        for c in self.samfiles:
            #strip of extension
            ct=trimdirext(c)
            #take last colwidth-2 chars
            cf=ct[-(colwidth-2):len(ct)]
            colheadtext=colheadtext+center(cf,colwidth)
        self.restext.component('columnheader').delete(1.0,END)
        self.restext.component('columnheader').insert('end',colheadtext)
        #print square prop matrix
        self.restext.clear()
        for c in range(cols):
            newline=''
            for i in range(len(self.samfiles)):
                vect=self.prop[c,i]
                val=valueclip_d(vect,4)
                val=center(val,colwidth)
                newline=newline+val
            self.restext.insert('end',newline+'\n')            
        self.restext.configure(Header_state = 'disabled')        
        if scale==0:
            setstatus(self.status,"Varimax rotation of selected components completed")
        else:
            setstatus(self.status,"Varimax rotation of selected scaled components completed")
        
    def updatevariance(self, *args):        
        #update variance when a selection is disabled
        eval=[]
        marker=0
        for np in self.compfiles:
            dat=self.compvars.get(np)
            if dat.active==0.5:
                eval.append(0)
                marker=1
            else:
                eval.append(dat.eigen)
        eval=array(eval)
        esum=sum(eval)
        ecsum=[]
        for i in range(len(eval)):
            ecsum.append(sum(eval[0:i+1]))
        ecsum=array(ecsum)
        varcomp=eval/esum
        varexp=ecsum/esum
        c=0
        for np in self.compfiles:
            wid=self.compvars.get(np)
            wid.var=varcomp[c]
            wid.vartot=varexp[c]
            self.compvars.update({np:wid})
            self.complist.item_configure(np,3,text=valueclip_d(wid.var,4))
            self.complist.item_configure(np,4,text=valueclip_d(wid.vartot,4))
            if c==0:
                fb=Tix.DisplayStyle(Tix.TEXT,fg='black',bg='white',selectforeground='white',refwindow=self.pca)
                fr=Tix.DisplayStyle(Tix.TEXT,fg='red',bg='white',selectforeground='red',refwindow=self.pca)
                if marker==1:
                    #make red
                    for i in range(5):
                        self.complist.item_configure(np,i,style=fr)
                else:
                    for i in range(5):
                        self.complist.item_configure(np,i,style=fb)                    
            c=c+1

    def createlibrary(self):
        global lastsavedir 
        #create dialog warning
        if tkMessageBox.askokcancel(title="Create File",message="This will create a PCA target transform\nlibrary file from all current samples.\nContinue?",parent=self.pca):
            i=1
        else:
            return     
        #check for samples
        nscans=len(self.samfiles)
        if nscans==0:
            setstatus(self.status,"Load library files as samples first!")            
            return
        #get filename
        nfn=ask_save_file('',lastsavedir)
        self.pca.focus_set()
        if nfn=="":
            setstatus(self.status,"Save parameters aborted")
            return
        sdir=rfind(nfn,os.sep)
        lastsavedir=nfn[:sdir]
        #open file
        #check for extension ending
        ext=rfind(nfn,".")
        if ext==-1:
            nfn=nfn+".plb"
        fid=open(nfn,"w")
        #save data-file parameters
        for np in self.samfiles:
            #select new sample
            dat=self.samvars.get(np)
            fid.write(dat.path+"\n")        
        fid.close()  
        setstatus(self.status,"Library file "+nfn+ " created")

    def targetlibrary(self):
        global lastreaddir
        resulttext=''
        #get filename
        fn=ask_for_file([("library files","*.plb"),("all files","*")],lastreaddir)
        self.pca.focus_set()
        if fn=="":
            setstatus(self.status,"Load parameters aborted")
            return
        #read file
        sdir=rfind(fn,os.sep)
        lastreaddir=fn[:sdir]
        #open file
        fid=open(fn,"r")
        param=fid.read()
        fid.close()
        #parse file...
        parsed=split(param,'\n')        
        #do transforms
        for tran in parsed:
            if tran!='':
                try:
                    #put name in target transform
                    self.targetfile.entry.delete(0,END)
                    self.targetfile.entry.insert(END,tran)
                    #do transform
                    self.dotargettrans()
                    #save results
                    CS=self.chisq.cget("text")[8:]
                    CS=CS[:8]
                    RV=self.rval.cget("text")[8:]
                    SP=self.spoil.cget("text")[7:]
                    resulttext=resulttext+CS+'\t\t'+RV+'\t\t'+SP+'\n'
                except:
                    #save error message
                    resulttext=resulttext+'\tFile error\n'                
        #display results in new window
        libreswin=Pmw.TextDialog(self.root,title='Library Transform Results',scrolledtext_hscrollmode='static',
                                      scrolledtext_vscrollmode='static',text_wrap='none',buttons=('Remove',),
                                      scrolledtext_columnheader=1,scrolledtext_rowheader=1,scrolledtext_rowcolumnheader=1,
                                      scrolledtext_rowheader_width=15,scrolledtext_rowcolumnheader_width=15,
                                      scrolledtext_usehullsize=1,scrolledtext_hull_width=550,
                                      scrolledtext_hull_height=300)
        libst=libreswin.component('scrolledtext')
        libst.component('rowcolumnheader').insert('end', 'Filename')
        libst.component('columnheader').insert('end','Chi Sq\t\tRval\t\tSPOIL')
        for tran in parsed:
            if tran!='':
                libst.component('rowheader').insert('end',trimdirext(tran)+'\n')
        libreswin.iconname('Library Target Results')
        libst.clear()
        libst.insert(END,resulttext)        
        
    def dotargettrans(self):
        #target transform
        #now look at data to transform
        tarfn=self.targetfile.entry.get()
        if tarfn=='':
            #put up message box
            tkMessageBox.showwarning(title="File Error",parent=self.pca,
                                     message='Choose target transform file')
            return
        if self.xmin.get()=='' or self.xmax.get()=='':
            tkMessageBox.showwarning(title="Parameter Error",message="Missing x-limits",parent=self.pca)
            return
        #do pca if it hasn't been done yet
        if self.compfiles==[]:
            self.dopca()
        #unselect last component if all are checked...
        i=0
        for np in self.compfiles:
            dat=self.compvars.get(np)
            if dat.active==1:
                i=i+1
        if i==len(self.compfiles):
            dat.active=0
            self.complist.item_configure(np,0,text=' ')
            self.updatevariance()            
        #load data
        self.targetdat.path=tarfn
        self.targetread()
        #make eigenvectors matrix selected
        self.ttransdat.xdat=[]
        self.ttransdat.ydat=[]
        rows=len(self.compvars[self.compfiles[0]].xdat)
        cols=len(self.compfiles)    
        ueigt=zeros((cols,rows),Float)
        i=0
        sumseceigsq=0
        spoilsum=0
        for np in self.compfiles:
            dat=self.compvars.get(np)
            if dat.active==1:
                #use this one
                newe=dat.ydat/dat.eigen
                a=i*rows
                b=a+rows
                put(ueigt,range(a,b),newe)
                i=i+1
                tempsp=sum(newe*self.targetdat.ydat)
                tempsp=tempsp/dat.eigen
                spoilsum=spoilsum+tempsp**2
            if dat.active!=1:
                sumseceigsq=sumseceigsq+dat.eigen**2
        numact=i
        ueig=transpose(ueigt)
        emat=matrixmultiply(ueig,ueigt)
        yd=self.targetdat.ydat
        tyd=matrixmultiply(emat,transpose(yd))
        tyd=transpose(tyd)
        self.ttransdat.ydat=tyd
        self.ttransdat.xdat=self.compvars[self.compfiles[0]].xdat
        #calcualte fit values...
        diff=self.ttransdat.ydat-self.targetdat.ydat
        diffsq=diff**2
        chisq=sum(diffsq)
        chisq=valueclip_d(chisq,6)
        setstatus(self.chisq,"Chi Sq: "+chisq)
        rvalue=sum(diffsq)/sum(self.targetdat.ydat**2)
        rvalue=valueclip_d(rvalue,6)
        setstatus(self.rval,"Rvalue: "+rvalue)
        spoilnum=len(yd)*(len(self.compfiles)-numact)*sum(diffsq)
        spoilden=(len(yd)-numact)*sumseceigsq*spoilsum
        if spoilden==0:
            spoil='NA'
        else:
            spoilrad=(spoilnum/spoilden)-1
            if spoilrad>0:
                spoil=sqrt(spoilrad)
            else:
                spoil=0
            spoil=valueclip_d(spoil,5)
        setstatus(self.spoil,"SPOIL: "+spoil)        
        #run plotter
        self.plotclick('Target Transform')

    def targetread(self):
        #read target data
        comchars=['#','!','%','*']
        #read file
        fn=self.targetdat.path
        self.targetdat.xdat=[]
        self.targetdat.ydat=[]
        fid=open(fn,"r")
        lines=fid.read().split('\n')
        for line in lines:
            if line !='' and line[0] not in comchars:
                dr=replace(line,',',' ')
                parsed=split(dr)
                if atof(self.xmin.get())<=atof(parsed[0]) and atof(parsed[0])<=atof(self.xmax.get()):
                    self.targetdat.xdat.append(atof(parsed[0]))
                    self.targetdat.ydat.append(atof(parsed[1]))
        fid.close()
        #turn into arrays
        self.targetdat.xdat=array(self.targetdat.xdat)
        self.targetdat.ydat=array(self.targetdat.ydat)
        #interpolate?  0.1 for XANES, 0.05 for EXAFS
        if self.nointerp.get()==0:
            if atof(self.xmin.get())<1000:
                newstep=0.05 #EXAFS
            else:
                newstep=0.1  #XANES
            try:
                interp=spline.Spline(self.targetdat.xdat,self.targetdat.ydat)
            except:
                tkMessageBox.showwarning(title="Parameter Error",message="x-limits out of data range",parent=self.pca)
                return
            self.targetdat.xdat=arange(atof(self.xmin.get()),atof(self.xmax.get()),newstep)
            self.targetdat.ydat=interp(self.targetdat.xdat)
        #apply k-weighting? 
        temp=self.targetdat.ydat*(self.targetdat.xdat**int(self.xwt.get()))
        self.targetdat.ydat=temp
        #update status bar
        npts=len(self.targetdat.xdat)
        cur=trimdir(fn)         
        setstatus(self.status,"Target File "+cur+" contains "+str(npts)+" data points")

    def savePCAcomponents(self):
        global lastreaddir,lastsavedir

        default="pcacomponents.txt"                
        fname=ask_save_file(default,lastsavedir)
        if fname != '':
            sdir=rfind(fname,os.sep)
            lastsavedir=fname[:sdir]
            #assemble data
            cmpdat=[]
            flag=0
            arlen=0
            totcols=0
            for np in self.compfiles:
                #select new sample
                dat=self.compvars.get(np)
                if dat.active==1:
                    if flag==0:
                        cmpdat.append(list(dat.xdat))
                        flag=1
                        arlen=len(dat.xdat)
                        totcols=totcols+1
                    cmpdat.append(list(dat.ydat))
                    totcols=totcols+1
            if arlen==0:
                print "Need selected components"
                return
            #save two column ascii w/ brief header
            fid=open(fname,"w")
            fid.write("# PCA Components "+fname+" created by SIXPack\n")
            for pt in range(arlen):
                for col in range(totcols):
                    fid.write(str(cmpdat[col][pt])+"\t")
                fid.write("\n")
            fid.close()

#start event handler
#PCAMain(root)
#root.mainloop()

