https://github.com/scw97/PiezoPaperExpressoCode
Raw File
Tip revision: bd8a58fa0e4f796e2ed0b72fe807862305b84b6b authored by scw97 on 05 February 2021, 20:18:22 UTC
changed to exact version used in paper
Tip revision: bd8a58f
expresso_gui_rough.py
# -*- coding: utf-8 -*-
"""
Created on Fri Dec 09 17:11:07 2016

@author: Fruit Flies
"""

import matplotlib
matplotlib.use('TkAgg')

import os 
import sys 

if sys.version_info[0] < 3:
    from Tkinter import *
    import tkFileDialog
    from ttk import *
    import tkMessageBox
else:
    from tkinter import *
    from tkinter.ttk import *
    from tkinter import filedialog as tkFileDialog
    from tkinter import messagebox as tkMessageBox

import h5py

import numpy as np
from scipy import interpolate

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
#import matplotlib.pyplot as plt
from matplotlib.figure import Figure

from load_hdf5_data import load_hdf5
from bout_analysis_func import bout_analysis
#from expresso_gui_params import guiParams

import csv
#------------------------------------------------------------------------------

class DirectoryFrame(Frame):
    """ Top UI frame containing the list of directories to be scanned. """

    def __init__(self, parent, col=0, row=0, filedir=None):
        Frame.__init__(self, parent)
                           
        self.lib_label = Label(parent, text='Directory list:') 
                               #foreground=guiParams['textcolor'], 
                               #background=guiParams['bgcolor'])
        self.lib_label.grid(column=col, row=row, padx=10, pady=2, sticky=NW)

        # listbox containing all selected additional directories to scan
        self.dirlist = Listbox(parent, width=64, height=8,
                               selectmode=SINGLE, exportselection=False)
                               #foreground=guiParams['textcolor'], 
                               #background=guiParams['listbgcolor'])
        self.dirlist.bind('<<ListboxSelect>>', self.on_select)
        self.dirlist.grid(column=col+1, row=row, padx=10, pady=2, sticky=W)

        self.btnframe = Frame(parent)
        self.btnframe.grid(column=col+2, row=row, sticky=NW)
        #self.btnframe.config(background=guiParams['bgcolor'])
        
        self.lib_addbutton =Button(self.btnframe, text='Add Directory',
                                   command= lambda: self.add_library(parent))
        self.lib_addbutton.grid(column=col, row=row, padx=10, pady=2,
                                sticky=NW)
        
        self.lib_delbutton = Button(self.btnframe, text='Remove Directory',
                                  command=self.rm_library, state=DISABLED)
                                   
        self.lib_delbutton.grid(column=col, row=row+1, padx=10, pady=2,
                                sticky=NW)

    def on_select(self, selection):
        """ Enable or disable based on a valid directory selection. """

        if self.dirlist.curselection():
            self.lib_delbutton.configure(state=NORMAL)
        else:
            self.lib_delbutton.configure(state=DISABLED)

    def add_library(self,parent):
        """ Insert every selected directory chosen from the dialog.
            Prevent duplicate directories by checking existing items. """

        dirlist = self.dirlist.get(0, END)
        newdir = Expresso.get_dir(parent)
        if newdir not in dirlist:
            self.dirlist.insert(END, newdir)

    def rm_library(self):
        """ Remove selected items from listbox when button in remove mode. """

        # Reverse sort the selected indexes to ensure all items are removed
        selected = sorted(self.dirlist.curselection(), reverse=True)
        for item in selected:
            self.dirlist.delete(item)

#------------------------------------------------------------------------------

class FileDataFrame(Frame):
    def __init__(self, parent, col=0, row=0):
        Frame.__init__(self, parent)

        self.list_label = Label(parent, text='Detected files:')
        self.list_label.grid(column=col, row=row, padx=10, pady=2, sticky=NW)

        self.filelistframe = Frame(parent) 
        self.filelistframe.grid(column=col+1, row=row, padx=10, pady=2, sticky=W)
        
        self.filelist = Listbox(self.filelistframe,  width=64, height=8, 
                                selectmode=SINGLE, exportselection=False)
        
        self.filelist.bind('<<ListboxSelect>>', self.on_select)
        #self.filelist.grid(column=col+1, row=row, padx=10, pady=2, sticky=W)
        
        self.hscroll = Scrollbar(self.filelistframe)
        self.hscroll.pack(side=BOTTOM, fill=X)

        self.vscroll = Scrollbar(self.filelistframe)
        self.vscroll.pack(side=RIGHT, fill=Y)
        
        self.filelist.config(xscrollcommand=self.hscroll.set,
                               yscrollcommand=self.vscroll.set)
        self.filelist.pack(side=TOP, fill=BOTH)
        
        self.hscroll.configure(orient=HORIZONTAL,
                               command=self.filelist.xview)
        self.vscroll.configure(orient=VERTICAL,
                               command=self.filelist.yview)

        self.btnframe = Frame(parent)
        self.btnframe.grid(column=col+2, row=row, sticky=NW)
        
        # button used to initiate the scan of the above directories
        self.scan_btn =Button(self.btnframe, text='Get HDF5 Files',
                                        command= lambda: self.add_files(parent))
        #self.scan_btn['state'] = 'disabled'                                
        self.scan_btn.grid(column=col, row=row, padx=10, pady=2,
                                sticky=NW)
                                
        # button used to remove the detected data
        self.remove_button = Button(self.btnframe, text='Remove Files')
        self.remove_button['state'] = 'disabled'
        self.remove_button['command'] = self.rm_files
        self.remove_button.grid(column=col, row=row+1, padx=10, pady=2,
                                sticky=NW)

        # label to show total files found and their size
        # this label is blank to hide it until required to be shown
        #self.total_label = Label(parent)
        #self.total_label.grid(column=col+1, row=row+2, padx=10, pady=2,
        #                      sticky=E)

    def on_select(self, selection):
        """ Enable or disable based on a valid directory selection. """

        if self.filelist.curselection():
            self.remove_button.configure(state=NORMAL)
        else:
            self.remove_button.configure(state=DISABLED)
            
    def add_files(self,parent):
        newfiles = Expresso.scan_dirs(parent)
        
        if len(newfiles) > 0:
            self.filelist.delete(0,END)
            Expresso.clear_xplist(parent)
            Expresso.clear_channellist(parent)
            for file in tuple(newfiles):
                self.filelist.insert(END,file)
    
    def rm_files(self):
        selected = sorted(self.filelist.curselection(), reverse=True)
        for item in selected:
            self.filelist.delete(item)
              
        
#------------------------------------------------------------------------------
        
class XPDataFrame(Frame):        
    def __init__(self, parent, col=0, row=0):
        Frame.__init__(self, parent)

        self.list_label = Label(parent, text='XP list:')
        self.list_label.grid(column=col+1, row=row, padx=10, pady=2, sticky=NW)

        self.xplistframe = Frame(parent) 
        self.xplistframe.grid(column=col+1, row=row+1, padx=10, pady=2, sticky=W)
        
        self.xplist = Listbox(self.xplistframe,  width=30, height=8, 
                              selectmode=SINGLE, exportselection=False)
        
        self.xplist.bind('<<ListboxSelect>>', self.on_select)
        #self.filelist.grid(column=col+1, row=row, padx=10, pady=2, sticky=W)
        
        self.hscroll = Scrollbar(self.xplistframe)
        self.hscroll.pack(side=BOTTOM, fill=X)

        self.vscroll = Scrollbar(self.xplistframe)
        self.vscroll.pack(side=RIGHT, fill=Y)
        
        self.xplist.config(xscrollcommand=self.hscroll.set,
                               yscrollcommand=self.vscroll.set)
        self.xplist.pack(side=TOP, fill=BOTH)
        
        self.hscroll.configure(orient=HORIZONTAL,
                               command=self.xplist.xview)
        self.vscroll.configure(orient=VERTICAL,
                               command=self.xplist.yview)

        self.btnframe = Frame(parent)
        self.btnframe.grid(column=col+1, row=row+2, sticky=NW)
        
        # button used to initiate the scan of the above directories
        self.unpack_btn =Button(self.btnframe, text='Unpack HDF5 Files',
                                        command= lambda: self.add_xp(parent))
        self.unpack_btn.grid(column=col, row=row, padx=10, pady=2,
                                sticky=NW)
                                
        # button used to remove the detected data
        self.remove_button = Button(self.btnframe, text='Remove Files')
        self.remove_button['state'] = 'disabled'
        self.remove_button['command'] = self.rm_xp
        self.remove_button.grid(column=col, row=row+1, padx=10, pady=2,
                                sticky=NW)

        # label to show total files found and their size
        # this label is blank to hide it until required to be shown
        #self.total_label = Label(parent)
        #self.total_label.grid(column=col+1, row=row+2, padx=10, pady=2,
        #                      sticky=E)

    def on_select(self, selection):
        """ Enable or disable based on a valid directory selection. """

        if self.xplist.curselection():
            self.remove_button.configure(state=NORMAL)
        else:
            self.remove_button.configure(state=DISABLED)
            
    def add_xp(self,parent):
        self.xplist.delete(0,END)
        Expresso.clear_channellist(parent)
        newxp = Expresso.unpack_files(parent)
        
        for xp in tuple(newxp):
            self.xplist.insert(END,xp)
    
    def rm_xp(self):
        selected = sorted(self.xplist.curselection(), reverse=True)
        for item in selected:
            self.xplist.delete(item)
    """    
    def on_select(self, selection):
           Enable or disable based on a valid hdf5 selection. 
        if self.fdata_tree.curselection():
            self.unpack_btn.configure(state=NORMAL)
        else:
            self.unpack_btn.configure(state=DISABLED)
    """
#------------------------------------------------------------------------------
    
class ChannelDataFrame(Frame):        
    def __init__(self, parent, col=0, row=0):
        Frame.__init__(self, parent)

        self.list_label = Label(parent, text='Channel list:')
        self.list_label.grid(column=col+1, row=row, padx=10, pady=2, sticky=N)
        
        self.channellistframe = Frame(parent) 
        self.channellistframe.grid(column=col+1, row=row+1, padx=10, pady=2, sticky=E)
        
        self.channellist = Listbox(self.channellistframe,  width=30, height=8,
                                   selectmode=SINGLE, exportselection=False)
        
        self.channellist.bind('<<ListboxSelect>>', self.on_select)
        #self.filelist.grid(column=col+1, row=row, padx=10, pady=2, sticky=W)
        
        self.hscroll = Scrollbar(self.channellistframe)
        self.hscroll.pack(side=BOTTOM, fill=X)

        self.vscroll = Scrollbar(self.channellistframe)
        self.vscroll.pack(side=RIGHT, fill=Y)
        
        self.channellist.config(xscrollcommand=self.hscroll.set,
                               yscrollcommand=self.vscroll.set)
        self.channellist.pack(side=TOP, fill=BOTH)
        
        self.hscroll.configure(orient=HORIZONTAL,
                               command=self.channellist.xview)
        self.vscroll.configure(orient=VERTICAL,
                               command=self.channellist.yview)

        self.btnframe = Frame(parent)
        self.btnframe.grid(column=col+1, row=row+2, sticky=NE)
        
        # button used to initiate the scan of the above directories
        self.unpack_btn =Button(self.btnframe, text='Unpack XP',
                                        command= lambda: self.add_channels(parent))
        self.unpack_btn.grid(column=col, row=row, padx=10, pady=2,
                                sticky=NE)
                                
        # button used to remove the detected data
        self.remove_button = Button(self.btnframe, text='Remove Files')
        self.remove_button['state'] = 'disabled'
        self.remove_button['command'] = self.rm_channel
        self.remove_button.grid(column=col, row=row+1, padx=10, pady=2,
                                sticky=NE)
                                
        self.selection_ind = []                        

        # label to show total files found and their size
        # this label is blank to hide it until required to be shown
        #self.total_label = Label(parent)
        #self.total_label.grid(column=col+1, row=row+2, padx=10, pady=2,
        #                      sticky=E)

    def on_select(self, selection):
        """ Enable or disable based on a valid directory selection. """

        if self.channellist.curselection():
            self.remove_button.configure(state=NORMAL)
            self.selection_ind = sorted(self.channellist.curselection(), reverse=True)
            #print(self.selection_ind)
        else:
            self.remove_button.configure(state=DISABLED)
            
    def add_channels(self,parent):
        self.channellist.delete(0,END)
        newchannels = Expresso.unpack_xp(parent)
        for channel in tuple(newchannels):
            self.channellist.insert(END,channel)
    
    def rm_channel(self):
        selected = sorted(self.channellist.curselection(), reverse=True)
        for item in selected:
            self.channellist.delete(item)
#------------------------------------------------------------------------------            
            
class FigureFrame(Frame):
    def __init__(self, parent,col=0,row=0):
        Frame.__init__(self, parent)
        
        #self.figure_label = Label(parent, text='Raw Data:')
        #self.figure_label.grid(column=col, row=row, padx=10, pady=2, sticky=NW)
       
        
        self.fig = Figure(figsize=(10, 5), dpi=100, facecolor = 'white')
        #self.fig = Figure()
        #self.subfig = self.fig.add_subplot(111)
        self.subplot0 = self.fig.add_subplot(211)
        self.subplot1 = self.fig.add_subplot(212, sharex=self.subplot0, sharey=self.subplot0)
        
        self.subplot0.set_ylabel('Liquid [nL]')
        self.subplot1.set_ylabel('Liquid [nL]')
       
        self.subplot1.set_xlabel('Time [units?]')
        
        self.subplot0.set_title('Raw Data')
        self.subplot1.set_title('Smoothed Data')
        #t = np.linspace(0.0, 3.0, 30)
        #s = np.sin(2*np.pi*t)
        
        #self.subplot0.plot(t, s)
        #self.subplot1.plot(t, 2*s)
        self.fig.set_tight_layout(True)
        
        self.canvas = FigureCanvasTkAgg(self.fig, parent)
        #self.canvas.show()
        self.canvas.get_tk_widget().grid(column=col, row=row, columnspan = 4, rowspan = 6, padx=10, pady=2, sticky=E)
        #self.canvas.get_tk_widget().configure(background='red')
        self.canvas.show()
        
        self.toolbar_frame = Frame(parent)
        self.toolbar_frame.grid(column = col, row=row+6, columnspan = 2, sticky= W)        
        self.toolbar = NavigationToolbar2TkAgg(self.canvas, self.toolbar_frame)
        self.toolbar.pack()
        self.toolbar.update()
        
        
        self.btnframe = Frame(parent)
        self.btnframe.grid(column=col+2,row=row+6,columnspan = 2, sticky = E)
        
        self.plot_btn =Button(self.btnframe, text='Plot data',
                                        command= lambda: self.plot_data(parent))
        self.plot_btn.grid(column=col, row=row, padx=10, pady=2,
                                sticky=NW)
        self.save_btn =Button(self.btnframe, text='Save results',
                                        command= lambda: self.save_results(parent))
        self.save_btn.grid(column=col+1, row=row, padx=10, pady=2,
                                sticky=NE)                        
        
        self.bouts = np.array([])
        
    def plot_data(self, parent):
        
        self.subplot0.cla()
        self.subplot1.cla()
        self.subplot0.set_ylabel('Liquid [nL]')
        self.subplot1.set_ylabel('Liquid [nL]')
        self.subplot1.set_xlabel('Time [units?]')
        self.subplot0.set_title('Raw Data')
        self.subplot1.set_title('Smoothed Data')
        
        dset, frames = Expresso.plot_channel(parent)
        
        if dset.size != 0:   
            dset_smooth, bouts, volumes = bout_analysis(dset,frames)
            
            self.bouts = bouts
            self.dset_smooth = dset_smooth
            
            self.subplot0.plot(np.squeeze(frames), np.squeeze(dset))
            self.subplot1.plot(frames, dset_smooth)
            
            for i in np.arange(bouts.shape[1]):
                self.subplot1.plot(frames[bouts[0,i]:bouts[1,i]], dset_smooth[bouts[0,i]:bouts[1,i]],'r-')
            
            self.subplot0.set_xlim([0,dset.size])
            self.subplot0.set_ylim([np.amin(dset),np.amax(dset)])
            self.fig.canvas.draw()
        else:
            tkMessageBox.showinfo(title='Error',
                                message='Invalid channel selection--no data in channel')
                                
    def save_results(self,parent):
        if sys.version_info[0] < 3:
            save_file = tkFileDialog.asksaveasfile(mode='wb', 
                            defaultextension=".csv")
            save_writer = csv.writer(save_file)
        else:
            save_filename = tkFileDialog.asksaveasfilename(defaultextension=".csv")
            save_file = open(save_filename, 'w', newline='')
            save_writer = csv.writer(save_file)
            
        bouts_transpose = np.transpose(self.bouts)
        if bouts_transpose.size > 0 :
            save_writer.writerow(['Bout Number'] + ['Bout Start'] + ['Bout End'])
            cc = 1            
            for row in bouts_transpose:
                new_row = np.insert(row,0,cc)
                save_writer.writerow(new_row)
                cc += 1
            
                                
            
#------------------------------------------------------------------------------

class Expresso(Tk):
    """The GUI and functions."""
    def __init__(self):
        Tk.__init__(self)
        
        # initialize important fields for retaining where we are in data space
        init_dirs = []
        self.initdirs = init_dirs
        
        datapath = []
        self.datadir_curr = datapath 
        
        filename = []
        self.filename_curr = filename 
        
        filekeyname = []
        self.filekeyname_curr = filekeyname
        
        grpnum = []
        self.grpnum_curr = grpnum 
        
        grp = []
        self.grp_curr = grp
        
        # run gui presets. may be unecessary
        self.init_gui()
        
        # initialize instances of frames created above
        self.dirframe = DirectoryFrame(self, col=0, row=0)
        self.fdata_frame = FileDataFrame(self, col=0, row=1)
        self.xpdata_frame = XPDataFrame(self, col=0, row=3)
        self.channeldata_frame = ChannelDataFrame(self, col=0, row=3)
        self.rawdata_plot = FigureFrame(self, col=4, row=0)
        
        for datadir in self.initdirs:
            self.dirframe.dirlist.insert(END, datadir)
        
        self.make_topmost()
        self.protocol("WM_DELETE_WINDOW", self.on_quit)
        
    def on_quit(self):
        """Exits program."""
        if tkMessageBox.askokcancel("Quit","Do you want to quit?"):
            self.destroy()
            self.quit()
    
    def make_topmost(self):
        """Makes this window the topmost window"""
        self.lift()
        self.attributes("-topmost", 1)
        self.attributes("-topmost", 0) 
        
    def init_gui(self):
        """Label for GUI"""
        self.title('Expresso Data Analysis (rough version)')
        
        
        """ Menu bar """
        self.option_add('*tearOff', 'FALSE')
        self.menubar = Menu(self)
 
        self.menu_file = Menu(self.menubar)
        self.menu_file.add_command(label='Exit', command=self.on_quit)
 
        self.menu_edit = Menu(self.menubar)
 
        self.menubar.add_cascade(menu=self.menu_file, label='File')
        self.menubar.add_cascade(menu=self.menu_edit, label='Edit')
 
        self.config(menu=self.menubar)
        #self.configure(background='dim gray')
        #self.tk_setPalette(background=guiParams['bgcolor'],
        #                   foreground=guiParams['textcolor']) 
                           
        
    @staticmethod
    def get_dir(self):
        """ Method to return the directory selected by the user which should
            be scanned by the application. """

        # get user specified directory and normalize path
        seldir = tkFileDialog.askdirectory(initialdir=sys.path[0])
        if seldir:
            seldir = os.path.abspath(seldir)
            self.datadir_curr = seldir
            return seldir
    
    @staticmethod        
    def scan_dirs(self):
        # build list of detected files from selected paths
        files = [] 
        temp_dirlist = list(self.dirframe.dirlist.get(0, END))
        selected_ind = sorted(self.dirframe.dirlist.curselection(), reverse=True)
        if len(selected_ind) < 1:
            tkMessageBox.showinfo(title='Error',
                                message='Please select directory from which to grab hdf5 files')
            return files                    
        # print(temp_dirlist)
        temp_dir = temp_dirlist[selected_ind[0]]
        for file in os.listdir(temp_dir):
            if file.endswith(".hdf5"):
                files.append(file)
                    
        self.datadir_curr = temp_dir
        
        if len(files) > 0:
            return files
        else:
            tkMessageBox.showinfo(title='Error',
                                message='No HDF5 files found.')
            files = []
            return files                    
            
    @staticmethod 
    def unpack_files(self):
        selected_ind = sorted(self.fdata_frame.filelist.curselection(), reverse=True)
        #print(selected_ind)
        selected = [] 
        for ind in selected_ind: 
            selected.append(self.fdata_frame.filelist.get(ind))
        
        temp_dirlist = list(self.dirframe.dirlist.get(0, END))
        for dir in temp_dirlist:
            filename = os.path.join(dir,selected[0])
            #print(filename)
            if os.path.isfile(filename):
                self.filename_curr = filename 
                f = h5py.File(filename,'r')
                fileKeyNames = list(f.keys())
                return fileKeyNames    
    @staticmethod 
    def unpack_xp(self):
        selected_ind = sorted(self.xpdata_frame.xplist.curselection(), reverse=True)
        f = h5py.File(self.filename_curr,'r')
        fileKeyNames = list(f.keys())
        #print(type(selected_ind))
        
        grp = f.require_group(fileKeyNames[selected_ind[0]])
        groupKeyNames = list(grp.keys())
        if groupKeyNames:
            self.filekeyname_curr = fileKeyNames[selected_ind[0]]
            self.grp_curr = grp 
            self.grpnum_curr = selected_ind[0]
            return groupKeyNames
    
    @staticmethod
    def clear_xplist(self):
        self.xpdata_frame.xplist.delete(0,END)
    
    @staticmethod
    def clear_channellist(self):
        self.channeldata_frame.channellist.delete(0,END)
    
    #@staticmethod
    #def clear_plot(self):
        #in progress
          
    @staticmethod
    def plot_channel(self):
        dset = load_hdf5(self.filename_curr,self.grpnum_curr,self.channeldata_frame.selection_ind[0])        
        
        dset_check = (dset != -1)
        if (np.sum(dset_check) == 0):
            dset = np.array([])
            frames = np.array([])
            print('Problem with loading data - invalid data set')
            return (dset, frames)    
            
        frames = np.arange(0,dset.size)
        
        dset = dset[dset_check]
        frames = frames[np.squeeze(dset_check)]
        
        new_frames = np.arange(0,np.max(frames)+1)
        sp_raw = interpolate.InterpolatedUnivariateSpline(frames, dset)
        dset = sp_raw(new_frames)
        frames = new_frames
        
        try:
            return (dset, frames)
        except NameError:
            dset = np.array([])
            frames = np.array([])
            print('Problem with loading data - name error')
            return (dset, frames)
    
       
""" Run main loop """
if __name__ == '__main__':
    Expresso().mainloop()
    
"""    
    root = Tk()
    Expresso(root)
    root.mainloop()
"""
back to top