# neuroplot.py -- Python script with routines for making pylab plots of neural
#   data.

# Script with pylab routines for plotting and processing neural data.
# 
# Last update: 10/31/11 (georgec)

# Import pylab so the functions and classes don't have to.
import pylab

#
# Data file I/O functions
#

# Load numpy arrays from a file. 
def ldnarrs(fstem):
   fname = 'savedpydata/%s.txt' % fstem
   return pylab.loadtxt(fname)

# Save numpy arrays to a file.
def svnarrs(fstem, X):
   fname = 'savedpydata/%s.txt' % fstem
   pylab.savetxt(fname, X)

#
# Data plotting functions
#

# Plot numpy arrays from a file.
# Note that tvec needs to be the first saved vector.  The other vectors
# will plot as separate traces. 
def plotnarrs(fstem, numtraces=1):
   vecsstr = 'tvec,vec'
   for ii in range(numtraces-1):
      vecsstr += ',vec%d' % (ii+2)
   exec('%s = ldnarrs("%s")' % (vecsstr, fstem))
   
   # Plot first plot.
   pylab.plot(tvec, vec)
   
   # Plot the remaining plots.
   for ii in range(numtraces-1):
      exec('pylab.plot(tvec,vec%d)' % (ii+2))

def plot_spike_times(tvec):
   xmarg = 10   # x display margin
   ymarg = 0.1  # y display margin
   if (tvec == []):
      return
   avec = pylab.ones(len(tvec))
   pylab.stem(tvec, avec)
#   pylab.xlim(-xmarg, sim_duration + xmarg)
   pylab.ylim(-ymarg, 1 + ymarg)
   pylab.title('Spike Times')
   pylab.xlabel('Time')

def plot_spike_raster(tvec, vec):
   xmarg = 10  # x display margin
   ymarg = 10  # y display margin
   marks = pylab.scatter(tvec, vec, marker='+')
   pylab.setp(marks, color='b')
#   pylab.xlim(-xmarg + simview_min_t, simview_max_t + xmarg)
   pylab.ylim(-ymarg, max(vec) + ymarg)
   pylab.title('Spike Events')
   pylab.xlabel('Time')
   pylab.ylabel('Unit ID #')

def plot_power_spect(x_fvec,x_pvec,logx=True,logy=False,\
   putupbands=True,putuplabels=True):
   freqs = pylab.copy(x_fvec)
   if ((not logx) and (not logy)):
      pylab.plot(freqs,x_pvec)
      if putuplabels:
         pylab.ylabel('Power')
   elif (logx and (not logy)):
      if (freqs[0] == 0.0):
         freqs[0] = 0.1
      pylab.semilogx(freqs,x_pvec)
      if putuplabels:
         pylab.ylabel('Power')
   elif ((not logx) and logy):
      pylab.semilogy(freqs,x_pvec)
      if putuplabels:
         pylab.ylabel('Log Power')
   else:
      if (freqs[0] == 0.0):
         freqs[0] = 0.1
      pylab.loglog(freqs,x_pvec)
      if putuplabels:
         pylab.ylabel('Log Power')
   if putuplabels:
      pylab.title('Spectrum')
   pylab.xlim(0,freqs[-1])
   if putuplabels:
      pylab.xlabel('Frequency (Hz)')
   if (putupbands):
      put_up_band_bounds(logx,logy)

def put_up_band_bounds(logx=True,logy=False):
   line = pylab.axvline(1, color='k')
   ax = line.get_axes()
   ymax = ax.get_ybound()[-1]
   pylab.axvline(4, color='k')
   pylab.axvline(7.5, color='k')
   pylab.axvline(14, color='k')
   pylab.axvline(30, color='k')
   pylab.axvline(70, color='k')
   if logx:
      if (not logy):
         pylab.text(1.75,ymax * 0.75,'delta')
         pylab.text(4.5,ymax * 0.75,'theta')
         pylab.text(8,ymax * 0.75,'alpha')
         pylab.text(17,ymax * 0.75,'beta')
         pylab.text(33,ymax * 0.75,'gamma')
      else:
         pylab.text(1.75,ymax * 0.10,'delta')
         pylab.text(4.5,ymax * 0.05,'theta')
         pylab.text(8,ymax * 0.10,'alpha')
         pylab.text(17,ymax * 0.05,'beta')
         pylab.text(33,ymax * 0.10,'gamma')
   else:
      if (not logy):
         pylab.text(1,ymax * 0.75,'delta')
         pylab.text(2,ymax * 0.7,'theta')
         pylab.text(8,ymax * 0.75,'alpha')
         pylab.text(20,ymax * 0.7,'beta')
         pylab.text(45,ymax * 0.75,'gamma')
      else:
         pylab.text(1,ymax * 0.10,'delta')
         pylab.text(2,ymax * 0.05,'theta')
         pylab.text(8,ymax * 0.10,'alpha')
         pylab.text(20,ymax * 0.05,'beta')
         pylab.text(45,ymax * 0.10,'gamma')

def plot_specgram(freqs, ts, Pxx, logz=False):
   if logz:
      pylab.imshow(pylab.log10(Pxx), origin='lower', aspect='auto', \
         extent=(0,ts[-1]*1000.0+self.ts[0]*1000.0,0,freqs[-1]))
      pylab.title('Log Spectrogram')
   else:
      pylab.imshow(Pxx, origin='lower', aspect='auto', \
         extent=(0,ts[-1]*1000.0+self.ts[0]*1000.0,0,freqs[-1]))
      pylab.title('Spectrogram')
   pylab.xlabel('Time (ms)')
   pylab.ylabel('Frequency (Hz)')
   pylab.spectral()
   pylab.colorbar()

def plot_band_specgram(bandnames, bts, Ptxx):
   pylab.imshow(Ptxx, origin='lower', aspect='auto', \
      extent=(0,bts[-1]*1000.0+self.bts[0]*1000.0,0,len(bandnames)), \
      interpolation='nearest')
   pylab.title('Band Spectrogram')
   pylab.xlabel('Time (ms)')
   pylab.ylabel('Spectral Band')
   pylab.yticks(pylab.arange(len(bandnames))+0.5, bandnames)
   pylab.spectral()
   pylab.colorbar()

def plot_specgram_spectdist(freqs,ts,Pxx,show='quartiles',logx=True,logy=False,putupbands=True):
   import scipy.stats

   # Get the mean, median, range, and standard deviation scores.
   mean_vec = Pxx.mean(axis=1)      # average over time
   std_vec = Pxx.std(axis=1)        # stdev over time
   upperstd_vec = mean_vec + std_vec
   lowerstd_vec = mean_vec - std_vec
   upperrng_vec = Pxx.max(axis=1)
   lowerrng_vec = Pxx.min(axis=1)

   # Calculate the 25th, 50th (median) and 75th percentile scores.
   p25_vec = pylab.zeros(len(mean_vec))
   median_vec = pylab.zeros(len(mean_vec))
   p75_vec = pylab.zeros(len(mean_vec))
   for ii in range(len(freqs)):
      p25_vec[ii] = scipy.stats.scoreatpercentile(Pxx[ii,:], 25)
      median_vec[ii] = scipy.stats.scoreatpercentile(Pxx[ii,:], 50)
      p75_vec[ii] = scipy.stats.scoreatpercentile(Pxx[ii,:], 75)

   if ((not logx) and (not logy)):
      if (show in ['meanstd','meanmed']):
         pylab.plot(freqs,mean_vec)
      if (show in ['meanmed','quartiles']):
         pylab.plot(freqs,median_vec)
      if (show in ['meanstd']):
         pylab.plot(freqs,upperstd_vec,'g')
         pylab.plot(freqs,lowerstd_vec,'g')
      if (show in ['quartiles']):
         pylab.plot(freqs,p75_vec,'g')
         pylab.plot(freqs,p25_vec,'g') 
      if (show in ['meanstd','meanmed','quartiles']):
         pylab.plot(freqs,upperrng_vec,'r')
         pylab.plot(freqs,lowerrng_vec,'r')
      pylab.ylabel('Power')
   elif (logx and (not logy)):
      if (freqs[0] == 0.0):
         freqs[0] = 0.1
      if (show in ['meanstd','meanmed']):
         pylab.semilogx(freqs,mean_vec)
      if (show in ['meanmed','quartiles']):
         pylab.semilogx(freqs,median_vec)
      if (show in ['meanstd']):
         pylab.semilogx(freqs,upperstd_vec,'g')
         pylab.semilogx(freqs,lowerstd_vec,'g')
      if (show in ['quartiles']):
         pylab.semilogx(freqs,p75_vec,'g')
         pylab.semilogx(freqs,p25_vec,'g') 
      if (show in ['meanstd','meanmed','quartiles']):
         pylab.semilogx(freqs,upperrng_vec,'r')
         pylab.semilogx(freqs,lowerrng_vec,'r')
      pylab.ylabel('Power')
   elif ((not logx) and logy):
      if (show in ['meanstd','meanmed']):
         pylab.semilogy(freqs,mean_vec)
      if (show in ['meanmed','quartiles']):
         pylab.semilogy(freqs,median_vec)
      if (show in ['meanstd']):
         pylab.semilogy(freqs,upperstd_vec,'g')
         pylab.semilogy(freqs,lowerstd_vec,'g')
      if (show in ['quartiles']):
         pylab.semilogy(freqs,p75_vec,'g')
         pylab.semilogy(freqs,p25_vec,'g') 
      if (show in ['meanstd','meanmed','quartiles']):
         pylab.semilogy(freqs,upperrng_vec,'r')
         pylab.semilogy(freqs,lowerrng_vec,'r')
      pylab.ylabel('Log Power')
   else:
      if (freqs[0] == 0.0):
         freqs[0] = 0.1
      if (show in ['meanstd','meanmed']):
         pylab.loglog(freqs,mean_vec)
      if (show in ['meanmed','quartiles']):
         pylab.loglog(freqs,median_vec)
      if (show in ['meanstd']):
         pylab.loglog(freqs,upperstd_vec,'g')
         pylab.loglog(freqs,lowerstd_vec,'g')
      if (show in ['quartiles']):
         pylab.loglog(freqs,p75_vec,'g')
         pylab.loglog(freqs,p25_vec,'g') 
      if (show in ['meanstd','meanmed','quartiles']):
         pylab.loglog(freqs,upperrng_vec,'r')
         pylab.loglog(freqs,lowerrng_vec,'r')
      pylab.ylabel('Log Power')
   pylab.title('Spectrogram Spectrum Distribution')
   pylab.xlim(0,freqs[-1])
   pylab.xlabel('Frequency (Hz)')
   if (putupbands):
      put_up_band_bounds(logx,logy)

#
# Data processing / analysis functions
#

def get_vec_subset(tvec, vec, mintvec=-1, maxtvec=-1, minvec=-1, maxvec=-1):
   if (mintvec == -1):
      mintvec = tvec.min()
   if (maxtvec == -1):
      maxtvec = tvec.max() + 0.0005  # allow inclusion of the max
   if (minvec == -1):
      minvec = vec.min()
   if (maxvec == -1):
      maxvec = vec.max() + 0.0005    # allow inclusion of the max
   filt_tvec = []
   filt_vec = []
   for ii in range(len(tvec)):
      # If the tvec and vec values are in the window, then include them.  Note
      # that the min boundaries include the minimum value, but the max bounds
      # do not include the max value, only values up to the max.
      if ((tvec[ii] >= mintvec) and (tvec[ii] < maxtvec) and \
         (vec[ii] >= minvec) and (vec[ii] < maxvec)):
         filt_tvec.append(tvec[ii])
         filt_vec.append(vec[ii])
   filt_tvec = pylab.array(filt_tvec)
   filt_vec = pylab.array(filt_vec)
   return filt_tvec, filt_vec

# Downsample a time sequence.
#   x_tvec - time stamps for sequence (units assumed to be in ms)
#   x_vec - amplitudes for sequence
#   newfs - new sample frequency (in Hz, must evenly divide the old fs)  
def downsample (x_tvec, x_vec, newfs):
   fsratio = (1000.0 / (x_tvec[1] - x_tvec[0])) / float(newfs)
   
   # If the fs ratio is invalid, give an error.
   if ((float(int(fsratio)) != fsratio) or (fsratio < 1)):
      print 'Error: The ratio of oldfs and newfs must be a positive integer'
      return None
      
   # Integerize the ratio.
   fsratio = int(fsratio)
   
   # Do the downsampling by averaging x_vec blockwise by blocks of size fsratio.
   npts = len(x_vec) / fsratio
   tvec = pylab.zeros(npts)
   vec = pylab.zeros(npts)
   for ii in range(npts):
      tvec[ii] = x_tvec[ii * fsratio]
      vec[ii] = x_vec[ii * fsratio:((ii+1) * fsratio)].mean()
      
   return tvec, vec

# Use convolution with a Hanning window to smooth a vector.
def smooth(x, win_size=11):
   y = pylab.zeros(x.size)
   smwin = pylab.hanning(win_size)
   smwin /= sum(smwin)  # scale the window so the total area is 1.0
   filtout = pylab.convolve(x,smwin,mode='valid')
   y[0:filtout.size] = filtout
   return y

def get_ave_fire_freqs(spks_tvec,spks_vec,num_units,fire_dur=0.0):
   if (fire_dur == 0.0):
      fire_dur = spks_tvec.max() - spks_tvec.min()
   fire_counts = pylab.zeros(num_units)
   for ii in range(len(spks_tvec)):
      fire_counts[spks_vec[ii]] += 1
   return fire_counts * 1000.0 / fire_dur

def get_unit_fire_bins(spks_tvec,spks_vec,num_units,fire_dur=0.0,bin_dur=100.0):
   if (fire_dur == 0.0):
      fire_dur = spks_tvec.max() - spks_tvec.min()
   num_bins = int(fire_dur / bin_dur)
   fire_bin_counts = pylab.zeros((num_units,num_bins))
   for ii in range(num_units):
      tvec, vec = get_vec_subset(spks_tvec, spks_vec, minvec=ii, maxvec=ii+1)
      n, bins = pylab.histogram(tvec,bins=num_bins,range=(0.0,fire_dur))
      fire_bin_counts[ii,:] = n[:]
   return bins,fire_bin_counts

# Given full spectrogram information, return a matrix where columns are all of 
# the time slices and rows are frequency band and the values are the sum, within 
# the time slice, of all of the power in that band.
def get_band_waterfall(freqs,ts,Pxx,normlz=False):
   bandnames = ['<Delta','Delta','Theta','Alpha','Beta','Gamma','>Gamma']
   bts = pylab.copy(ts)
   Ptxx = pylab.zeros((len(bandnames),len(ts)))
   for tt in range(len(ts)):
      for ff in range(len(freqs)):
         if (freqs[ff] < 1.0):
            Ptxx[0,tt] += Pxx[ff,tt]  # sub-delta
         elif ((freqs[ff] >= 1.0) and (freqs[ff] < 4.0)):
            Ptxx[1,tt] += Pxx[ff,tt]  # delta
         elif ((freqs[ff] >= 4.0) and (freqs[ff] < 7.5)):
            Ptxx[2,tt] += Pxx[ff,tt]  # theta
         elif ((freqs[ff] >= 7.5) and (freqs[ff] < 14.0)):
            Ptxx[3,tt] += Pxx[ff,tt]  # alpha
         elif ((freqs[ff] >= 14.0) and (freqs[ff] < 30.0)):
            Ptxx[4,tt] += Pxx[ff,tt]  # beta
         elif ((freqs[ff] >= 30.0) and (freqs[ff] < 70.0)):
            Ptxx[5,tt] += Pxx[ff,tt]  # gamma
         else:
            Ptxx[6,tt] += Pxx[ff,tt]  # super-gamma
      if (normlz):
         Ptxx[:,tt] /= sum(Ptxx[:,tt])
   return bandnames,bts,Ptxx

#
# Data plotting / browsing classes
#

class SpecgramBrowser:
   """
   Spectrogram browser for looking at spectra time-slices in a waterfall plot.

   Methods:
     constructor(xxx):

   Attributes:
     count (static): number of instances

   Usage:
   >>> sb = SpecgramBrowser(freqs,ts,Pxx)
   """

   count = 0

   def __init__(self, freqs, ts, Pxx, Pxx2=None, logz=False, logx=True, logy=False, \
      putupbands=True, nointerp=False):
      # Set up browser parameters.
      self.freqs = freqs
      self.ts = ts
      self.Pxx = Pxx
      self.Pxx2 = Pxx2
      self.logz = logz
      self.logx = logx
      self.logy = logy
      self.ymaxlock = False
      self.putupbands = putupbands
      self.nointerp = nointerp
      self.specgram_fig = pylab.figure()  # handle to specgram_browser spectrogram figure
      if (self.Pxx2 == None):
         self.specgram_fig2 = 0   # handle to specgram_browser spectrogram 2 figure
      else:
         self.specgram_fig2 = pylab.figure()  # handle to specgram_browser spectrogram figure
      self.spect_fig = pylab.figure()     # handle to specgram_browser spectrum figure
      self.tindex = 0           # index to the time slice to be shown in the spectrum figure/s
      self.specgram_mark = 0    # handle to spectrogram time slice marker
      self.specgram_mark2 = 0   # handle to spectrogram 2 time slice marker

      # Increment the instance count.
      SpecgramBrowser.count += 1 

      # Draw the initial spectrogram/s.
      self._draw_specgram()

      # Draw the initial spectrum/a.
      self._draw_spect()

      # Attach an onclick event to grab mouse clicks for spectrogram window.
      cid = self.specgram_fig.canvas.mpl_connect('button_press_event', self._onclick)

      # Attach an onkey event to grab key presses for spectrogram window.
      cid2 = self.specgram_fig.canvas.mpl_connect('key_press_event', self._onkey)

      if (self.Pxx2 != None):
         # Attach an onclick event to grab mouse clicks for spectrogram window.
         cid3 = self.specgram_fig2.canvas.mpl_connect('button_press_event', self._onclick)

         # Attach an onkey event to grab key presses for spectrogram window.
         cid4 = self.specgram_fig2.canvas.mpl_connect('key_press_event', self._onkey)

      # Attach an onkey event to grab key presses for spectrum window.
      cid5 = self.spect_fig.canvas.mpl_connect('key_press_event', self._onkey2)

   def _draw_specgram(self):
      def figdrawer(self, Pxx):
         pylab.clf()
         if self.logz:
            if self.nointerp:
               pylab.imshow(pylab.log10(Pxx), origin='lower', aspect='auto', \
                  extent=(0,self.ts[-1]*1000.0+self.ts[0]*1000.0,0,self.freqs[-1]), \
                  interpolation='nearest')
            else:
               pylab.imshow(pylab.log10(Pxx), origin='lower', aspect='auto', \
                  extent=(0,self.ts[-1]*1000.0+self.ts[0]*1000.0,0,self.freqs[-1]))
            pylab.title('Log Spectrogram')
         else:
            if self.nointerp:
               pylab.imshow(Pxx, origin='lower', aspect='auto', \
                  extent=(0,self.ts[-1]*1000.0+self.ts[0]*1000.0,0,self.freqs[-1]), \
                  interpolation='nearest')
            else:
               pylab.imshow(Pxx, origin='lower', aspect='auto', \
                  extent=(0,self.ts[-1]*1000.0+self.ts[0]*1000.0,0,self.freqs[-1]))
            pylab.title('Spectrogram')
         pylab.xlabel('Time (ms)')
         pylab.ylabel('Frequency (Hz)')
         pylab.spectral()
         pylab.colorbar()

      pylab.figure(self.specgram_fig.number)
      figdrawer(self, self.Pxx)
      self.specgram_mark = pylab.axvline(self.ts[self.tindex] * 1000.0,color='r')
      if (self.Pxx2 != None):
         pylab.figure(self.specgram_fig2.number)
         figdrawer(self, self.Pxx2)
         self.specgram_mark2 = pylab.axvline(self.ts[self.tindex] * 1000.0,color='r')

   def _move_specgram_mark(self):
      # Reset the line marker according to the new time slice index.  
      self.specgram_mark.set_xdata(pylab.array([self.ts[self.tindex] * 1000.0,self.ts[self.tindex] * 1000.0]))

      # Redraw the spectrogram canvas.
      self.specgram_fig.canvas.draw()

      # If we have another spectrogram...
      if (self.Pxx2 != None):
         # Reset the line marker according to the new time slice index.  
         self.specgram_mark2.set_xdata(pylab.array([self.ts[self.tindex] * \
            1000.0,self.ts[self.tindex] * 1000.0]))

         # Redraw the spectrogram canvas.
         self.specgram_fig2.canvas.draw()

   def _draw_spect(self):
      pylab.figure(self.spect_fig.number)
      pylab.clf()
      freqs2 = pylab.copy(self.freqs)
      x_pvec = pylab.copy(self.Pxx[:,self.tindex])
      if (self.Pxx2 != None):
         x_pvec2 = pylab.copy(self.Pxx2[:,self.tindex])
      if ((not self.logx) and (not self.logy)):
         pylab.plot(freqs2,x_pvec)
         if (self.Pxx2 != None):
            pylab.plot(freqs2,x_pvec2,'r')
         pylab.ylabel('Power')
      elif (self.logx and (not self.logy)):
         if (freqs2[0] == 0.0):
            freqs2[0] = 0.1
         pylab.semilogx(freqs2,x_pvec)
         if (self.Pxx2 != None):
            pylab.semilogx(freqs2,x_pvec2,'r')
         pylab.ylabel('Power')
      elif ((not self.logx) and self.logy):
         pylab.semilogy(freqs2,x_pvec)
         if (self.Pxx2 != None):
            pylab.semilogy(freqs2,x_pvec2,'r')
         pylab.ylabel('Log Power')
      else:
         if (freqs2[0] == 0.0):
            freqs2[0] = 0.1
         pylab.loglog(freqs2,x_pvec)
         if (self.Pxx2 != None):
            pylab.loglog(freqs2,x_pvec2,'r')
         pylab.ylabel('Log Power')
      if (self.Pxx2 == None):
         pylab.title('Spectrum at %.2f s' % self.ts[self.tindex])
      else:
         pylab.title('Spectra at %.2f s' % self.ts[self.tindex])
      pylab.xlim(0,freqs2[-1])
      if (self.ymaxlock):
         maxlock = self.Pxx.max()
         if (self.Pxx2 != None):
            maxlock = max(maxlock, self.Pxx2.max())
         maxlock *= 1.0
         pylab.ylim(0,maxlock)
      pylab.xlabel('Frequency (Hz)')
      if (self.putupbands):
         put_up_band_bounds(self.logx,self.logy)

   def _onclick(self,event):
      print 'Selected...'
#      print 'button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % \
#         (event.button, event.x, event.y, event.xdata, event.ydata)

      # Create freq bin upper boundaries and array of indices where value 
      # falls in bin.
      fbinrad = self.freqs[1] / 2.0
      fbinubs = self.freqs + fbinrad
      fis = pylab.find(fbinubs >= event.ydata)

      # Create time bin upper boundaries and array of indices where value
      # falls in bin.
      tbinrad = (self.ts[1] - self.ts[0]) / 2.0
      tbinubs = (self.ts + tbinrad) * 1000.0
      tis = pylab.find(tbinubs >= event.xdata)

      # Show the bin and value.
      print 'freq bin=%f, time bin=%f, value=%f' % (self.freqs[fis[0]], self.ts[tis[0]], self.Pxx[fis[0]][tis[0]])

      # Switch the spectrum view to the corresponding time slice.
      self.tindex = tis[0]
      self._draw_spect()

      # Redraw the spectrogram (moving the marker).
      self._draw_specgram()

   def _onkey(self,event):
      # Note: for reasons I don't understand, Tkinter callbacks trap the 
      # following keystrokes, so I cannot use them:
      #   's'
      #   'l'   
#      print 'you pressed', event.key, event.xdata, event.ydata
      if (event.key == 'left'):
         if (self.tindex > 0):
            self.tindex -= 1
         self._draw_spect()
         self._draw_specgram()
      elif (event.key == 'right'):
         if (self.tindex < len(self.ts)-1):
            self.tindex += 1
         self._draw_spect()
         self._move_specgram_mark()
      elif (event.key == 'i'):
         self.nointerp = not self.nointerp
         self._draw_specgram()

   def _onkey2(self,event):
      # Note: for reasons I don't understand, Tkinter callbacks trap the 
      # following keystrokes, so I cannot use them:
      #   's'
      #   'l'  
#      print 'you pressed', event.key, event.xdata, event.ydata
      if (event.key == 'left'):
         if (self.tindex > 0):
            self.tindex -= 1
         self._draw_spect()
         self._move_specgram_mark()
      elif (event.key == 'right'):
         if (self.tindex < len(self.ts)-1):
            self.tindex += 1
         self._draw_spect()
         self._move_specgram_mark()
      elif (event.key == 'x'):
         self.logx = not self.logx
         self._draw_spect()
      elif (event.key == 'y'):
         self.logy = not self.logy
         self._draw_spect()
      elif (event.key == 'b'):
         self.putupbands = not self.putupbands
         self._draw_spect()
      elif (event.key == 'm'):
         self.ymaxlock = not self.ymaxlock
         self._draw_spect()