text: from . import plotting
  from . import datastructure
  from . import acquisition
  from . import analysis
  from . import streams
  from . import file
  import time
  
  import numpy as np
  from IPython.display import display
  from ipywidgets import Button, HBox, VBox, Output, FloatText, IntText, Dropdown, IntSlider, Layout, Label, Checkbox,ToggleButton
  
  
  #%%
  class InteractiveLogging():
      def __init__(self,settings,test_name=None,default_window='hanning'):
          
          if default_window is None:
              default_window = 'None'
          self.settings = settings
          self.test_name = test_name
          self.dataset = datastructure.DataSet()
          
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out = Output()
          self.out_top = Output(layout=Layout(height='120px'))
          
          
          # Initialise variables
          self.current_view = 'Time'    
          self.N_frames = 1
          self.overlap = 0.5
          self.iw_fft_power = 0
          self.iw_tf_power = 0
          self.legend_loc = 'lower right'
  
          # puts plot inside widget so can have buttons next to plot
          self.outplot = Output(layout=Layout(width='85%'))
                  
          # BUTTONS
          items_measure = ['Log Data','Delete Last Measurement','Reset (clears all data)','Load Data']
          items_view = ['View Time', 'View FFT', 'View TF']
          items_calc = ['Calc FFT', 'Calc TF', 'Calc TF average']
          items_axes = ['xmin','xmax','ymin','ymax']
          items_save = ['Save Dataset','Save Figure']
          items_iw   = ['multiply iw','divide iw']
          
          items_legend = ['Left','on/off','Right']
          
          
          
          self.buttons_measure= [Button(description=i, layout=Layout(width='33%')) for i in items_measure]
          self.buttons_measure[0].button_style ='success'
          self.buttons_measure[0].style.font_weight = 'bold'
          self.buttons_measure[1].button_style ='warning'
          self.buttons_measure[1].style.font_weight = 'bold'
          self.buttons_measure[2].button_style ='danger'
          self.buttons_measure[2].style.font_weight = 'bold'
          self.buttons_measure[3].button_style ='primary'
          self.buttons_measure[3].style.font_weight = 'bold'
          
          self.buttons_view = [Button(description=i, layout=Layout(width='95%')) for i in items_view]
          self.buttons_view[0].button_style ='info'
          self.buttons_view[1].button_style ='info'
          self.buttons_view[2].button_style ='info'
          
          self.buttons_calc = [Button(description=i, layout=Layout(width='99%')) for i in items_calc]
          self.buttons_calc[0].button_style ='primary'
          self.buttons_calc[1].button_style ='primary'
          self.buttons_calc[2].button_style ='primary'
  
          
          self.buttons_iw_fft = [Button(description=i, layout=Layout(width='50%')) for i in items_iw]
          self.buttons_iw_fft[0].button_style = 'info'
          self.buttons_iw_fft[1].button_style = 'info'
          
          self.buttons_iw_tf = [Button(description=i, layout=Layout(width='50%')) for i in items_iw]
          self.buttons_iw_tf[0].button_style = 'info'
          self.buttons_iw_tf[1].button_style = 'info'
          
          self.buttons_match = Button(description='Match Amplitudes', layout=Layout(width='99%'))
          self.buttons_match.button_style = 'info'
          
          self.buttons_save = [Button(description=i, layout=Layout(width='50%')) for i in items_save]
          self.buttons_save[0].button_style = 'success'
          self.buttons_save[0].style.font_weight = 'bold'
          self.buttons_save[1].button_style = 'success'
          self.buttons_save[1].style.font_weight = 'bold'
          
          self.button_warning = Button(description='WARNING: Data may be clipped. Press here to delete last measurement.', layout=Layout(width='100%'))
          self.button_warning.button_style = 'danger'
          self.button_warning.style.font_weight='bold'
          
          self.button_X = Button(description='Auto X', layout=Layout(width='95%'))
          self.button_Y = Button(description='Auto Y', layout=Layout(width='95%'))
          self.button_X.button_style ='success'
          self.button_Y.button_style ='success'
          
          self.buttons_legend = [Button(description=i, layout=Layout(width='31%')) for i in items_legend]
          self.buttons_legend_toggle = ToggleButton(description='Click-and-drag', layout=Layout(width='95%',alignitems='start'))
          
          # TEXT/LABELS/DROPDOWNS
          self.item_iw_fft_label = Label(value='iw power={}'.format(0),layout=Layout(width='100%'))
          self.item_iw_tf_label  = Label(value='iw power={}'.format(0),layout=Layout(width='100%'))
          self.item_label = Label(value="Frame length = {:.2f} seconds.".format(settings.stored_time/(self.N_frames-self.overlap*self.N_frames+self.overlap)))
          self.item_axis_label = Label(value="Axes control:",layout=Layout(width='95%'))
          self.item_view_label = Label(value="View data:",layout=Layout(width='95%'))
          self.item_legend_label = Label(value="Legend position:",layout=Layout(width='95%'))
          self.item_blank_label = Label(value="",layout=Layout(width='95%'))
          
          self.text_axes = [FloatText(value=0,description=i, layout=Layout(width='95%')) for i in items_axes]
          self.text_axes =  [self.button_X]+[self.button_Y] + self.text_axes
          self.drop_window = Dropdown(options=['None', 'hanning'],value=default_window,description='Window:', layout=Layout(width='99%'))
          self.slide_Nframes = IntSlider(value=1,min=1,max=30,step=1,description='N_frames:',continuous_update=True,readout=False, layout=Layout(width='99%'))
          self.text_Nframes = IntText(value=1,description='N_frames:', layout=Layout(width='99%'))
          
          # VERTICAL GROUPS
          
          group0 = VBox([self.buttons_calc[0],self.drop_window,HBox(self.buttons_iw_fft)],layout=Layout(width='33%'))
          group1 = VBox([self.buttons_calc[1],self.drop_window,self.slide_Nframes,self.text_Nframes,self.item_label,HBox(self.buttons_iw_tf),self.buttons_match],layout=Layout(width='33%'))
          group2 = VBox([self.buttons_calc[2],self.drop_window,HBox(self.buttons_iw_tf),self.buttons_match],layout=Layout(width='33%'))
          
          group_view = VBox([self.item_axis_label]+self.text_axes+[self.item_legend_label,self.buttons_legend_toggle]+[HBox(self.buttons_legend),self.item_view_label]+self.buttons_view,layout=Layout(width='20%'))
          
          # ASSEMBLE
          display(self.out_top)
          display(HBox([self.button_warning]))
          display(HBox(self.buttons_measure))
          display(HBox([self.outplot,group_view]))
          display(HBox([group0,group1,group2]))
          display(HBox(self.buttons_save))
          self.button_warning.layout.visibility='hidden'
          
          # second part to putting plot inside widget
          with self.outplot:
              self.p = plotting.PlotData(figsize=(7.5,4))
          
          
          ## Make buttons/boxes interactive
          
          self.text_axes[2].observe(self.xmin, names='value')
          self.text_axes[3].observe(self.xmax, names='value')
          self.text_axes[4].observe(self.ymin, names='value')
          self.text_axes[5].observe(self.ymax, names='value')
          
          self.button_X.on_click(self.auto_x)
          self.button_Y.on_click(self.auto_y)
          
          self.buttons_legend[0].on_click(self.legend_left)
          self.buttons_legend[1].on_click(self.legend_onoff)
          self.buttons_legend[2].on_click(self.legend_right)
          self.buttons_legend_toggle.observe(self.legend_toggle)
          
          self.slide_Nframes.observe(self.nframes_slide)
          self.text_Nframes.observe(self.nframes_text)
          
          self.buttons_measure[0].on_click(self.measure)
          self.buttons_measure[1].on_click(self.undo)
          self.buttons_measure[2].on_click(self.reset)
          self.buttons_measure[3].on_click(self.load_data)
          
          self.buttons_view[0].on_click(self.view_time)
          self.buttons_view[1].on_click(self.view_fft)
          self.buttons_view[2].on_click(self.view_tf)
          
          self.buttons_calc[0].on_click(self.fft)
          self.buttons_calc[1].on_click(self.tf)
          self.buttons_calc[2].on_click(self.tf_av)
          
          self.buttons_iw_fft[0].on_click(self.xiw_fft)
          self.buttons_iw_fft[1].on_click(self.diw_fft)
          self.buttons_iw_tf[0].on_click(self.xiw_tf)
          self.buttons_iw_tf[1].on_click(self.diw_tf)
          
          self.buttons_match.on_click(self.match)
          
          self.buttons_save[0].on_click(self.save_data)
          self.buttons_save[1].on_click(self.save_fig)
          
          self.button_warning.on_click(self.undo)
          
          self.refresh_buttons()
          
          
          # Put output text at bottom of display
          display(self.out)
          
          with self.out_top:
              try:
                  streams.start_stream(settings)
                  self.rec = streams.REC
              except:
                  print('Data stream not initialised.')
                  print('Possible reasons: pyaudio or PyDAQmx not installed, or acquisition hardware not connected.')
                  print('Please note that it won''t be possible to log data.')
              
          
      
      def xmin(self,v):
          xmin = self.text_axes[2].value
          xlim = self.p.ax.get_xlim()
          self.p.ax.set_xlim([xmin,xlim[1]])
          
      def xmax(self,v):
          xmax = self.text_axes[3].value
          xlim = self.p.ax.get_xlim()
          self.p.ax.set_xlim([xlim[0],xmax])
          
      def ymin(self,v):
          ymin = self.text_axes[4].value
          ylim = self.p.ax.get_ylim()
          self.p.ax.set_ylim([ymin,ylim[1]])
          
      def ymax(self,v):
          ymax = self.text_axes[5].value
          ylim = self.p.ax.get_ylim()
          self.p.ax.set_ylim([ylim[0],ymax])
          
      def auto_x(self,b):
          self.p.auto_x()
          
      def auto_y(self,b):
          self.p.auto_y()
          
      def legend_left(self,b):
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              self.legend_loc = 'lower left'
              self.p.update_legend(self.legend_loc)
          
      def legend_right(self,b):
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              self.legend_loc = 'lower right'
              self.p.update_legend(self.legend_loc)
              
      def legend_onoff(self,b):
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              visibility = self.p.ax.get_legend().get_visible()
              self.p.ax.get_legend().set_visible(not visibility)
                  
      def legend_toggle(self,b):
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              self.p.ax.get_legend().set_visible(True)
              self.p.legend.set_draggable(self.buttons_legend_toggle.value)
              
          
      def nframes_slide(self,v):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              self.N_frames = self.slide_Nframes.value
              self.text_Nframes.value = self.N_frames
              if len(self.dataset.time_data_list) is not 0:
                  stored_time = self.dataset.time_data_list[0].settings.stored_time
              elif len(self.dataset.tf_data_list) is not 0:
                  stored_time = self.dataset.tf_data_list[0].settings.stored_time
              else:
                  stored_time = 0
                  print('Time or TF data settings not found')
                  
              self.item_label.value = "Frame length = {:.2f} seconds.".format(stored_time/(self.N_frames-self.overlap*self.N_frames+self.overlap))
              if self.current_view is 'TF':
                  self.tf(None)
          
      def nframes_text(self,v):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              self.N_frames = self.text_Nframes.value
              self.slide_Nframes.value = self.N_frames
              if len(self.dataset.time_data_list) is not 0:
                  stored_time = self.dataset.time_data_list[0].settings.stored_time
              elif len(self.dataset.tf_data_list) is not 0:
                  stored_time = self.dataset.tf_data_list[0].settings.stored_time
              else:
                  stored_time = 0
                  print('Time or TF data settings not found')
              self.item_label.value = "Frame length = {:.2f} seconds.".format(stored_time/(self.N_frames-self.overlap*self.N_frames+self.overlap))
              if self.current_view is 'TF':
                  self.tf(None)
          
      def measure(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              self.rec.trigger_detected = False
              self.buttons_measure[0].button_style =''
              if self.settings.pretrig_samples is None:
                  self.buttons_measure[0].description = 'Logging ({}s)'.format(self.settings.stored_time)
              else:
                  self.buttons_measure[0].description = 'Logging ({}s, with trigger)'.format(self.settings.stored_time)
              
              d = acquisition.log_data(self.settings,test_name=self.test_name, rec=self.rec)
              self.dataset.add_to_dataset(d.time_data_list)
              N = len(self.dataset.time_data_list)
              self.p.update(self.dataset.time_data_list,sets=[N-1],channels='all')
              self.p.auto_x()
  #            self.p.auto_y()
              self.p.ax.set_ylim([-1,1])
              self.current_view='Time'
              self.buttons_measure[0].button_style ='success'
              self.buttons_measure[0].description = 'Log Data'
              
              if np.any(np.abs(d.time_data_list[-1].time_data) > 0.95):
                  self.button_warning.layout.visibility = 'visible'
              else:
                  self.button_warning.layout.visibility = 'hidden'
              
              xlim = self.p.ax.get_xlim()
              ylim =  self.p.ax.get_ylim()
              self.text_axes[2].value = xlim[0]
              self.text_axes[3].value = xlim[1]
              self.text_axes[4].value = ylim[0]
              self.text_axes[5].value = ylim[1]
              self.refresh_buttons()
          
      def undo(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              self.dataset.remove_last_data_item('TimeData')
              self.dataset.freq_data_list = datastructure.FreqDataList()
              self.dataset.tf_data_list = datastructure.TfDataList()
              N = len(self.dataset.time_data_list)
              self.p.update(self.dataset.time_data_list,sets=[N-1],channels='all')
              self.button_warning.layout.visibility = 'hidden'
              self.refresh_buttons()
              
      def reset(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.dataset = datastructure.DataSet()
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              N = len(self.dataset.time_data_list)
              self.p.update(self.dataset.time_data_list,sets=[N-1],channels='all')
              self.refresh_buttons()
          
      
                  
      def load_data(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              d = file.load_data()
              if d is not None:
                  self.dataset.add_to_dataset(d.time_data_list)
                  self.dataset.add_to_dataset(d.freq_data_list)
                  self.dataset.add_to_dataset(d.tf_data_list)
                  self.dataset.add_to_dataset(d.cross_spec_data_list)
                  self.dataset.add_to_dataset(d.sono_data_list)
              else:
                  print('No data loaded')
              
              if len(self.dataset.time_data_list) is not 0:
                  self.p.update(self.dataset.time_data_list,sets='all',channels='all')
              elif len(self.dataset.freq_data_list) is not 0:
                  self.p.update(self.dataset.freq_data_list,sets='all',channels='all')
              elif len(self.dataset.tf_data_list) is not 0:
                  self.p.update(self.dataset.tf_data_list,sets='all',channels='all')
              else:
                  print('No data to view')
             
              self.refresh_buttons()
          
              self.p.auto_x()
              self.p.auto_y()
              
              xlim = self.p.ax.get_xlim()
              ylim = self.p.ax.get_ylim()
              self.text_axes[2].value = xlim[0]
              self.text_axes[3].value = xlim[1]
              self.text_axes[4].value = ylim[0]
              self.text_axes[5].value = ylim[1]
              
          
          
      def view_time(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              self.refresh_buttons()
              N = len(self.dataset.time_data_list)
              if N is not 0:
                  self.p.update(self.dataset.time_data_list)
                  if self.current_view is not 'Time':
                      self.current_view = 'Time'
                      self.p.auto_x()
                      self.p.auto_y()
  
                  xlim = self.p.ax.get_xlim()
                  ylim = self.p.ax.get_ylim()
                  self.text_axes[2].value = xlim[0]
                  self.text_axes[3].value = xlim[1]
                  self.text_axes[4].value = ylim[0]
                  self.text_axes[5].value = ylim[1]
              else:
                  print('no time data to display')
                  
      def view_fft(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              N = len(self.dataset.freq_data_list)
              self.refresh_buttons()
              if N is not 0:
                  self.p.update(self.dataset.freq_data_list)
                  if self.current_view is not 'FFT':
                      self.current_view = 'FFT'
                      self.p.auto_x()
                      self.p.auto_y() 
                      
                  xlim = self.p.ax.get_xlim()
                  ylim = self.p.ax.get_ylim()
                  self.text_axes[2].value = xlim[0]
                  self.text_axes[3].value = xlim[1]
                  self.text_axes[4].value = ylim[0]
                  self.text_axes[5].value = ylim[1]
              else:
                  print('no FFT data to display')
                  
      def view_tf(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              N = len(self.dataset.tf_data_list)
              self.refresh_buttons()
              if N is not 0:
                  self.p.update(self.dataset.tf_data_list)
                  if self.current_view is not 'TF':
                      self.current_view = 'TF'
                      self.p.auto_x()
                      self.p.auto_y()
                  xlim = self.p.ax.get_xlim()
                  ylim = self.p.ax.get_ylim()
                  self.text_axes[2].value = xlim[0]
                  self.text_axes[3].value = xlim[1]
                  self.text_axes[4].value = ylim[0]
                  self.text_axes[5].value = ylim[1]
              else:
                  print('no TF data to display')
              
      def fft(self,b):
          window = self.drop_window.value
          if window is 'None':
              window = None
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              self.dataset.calculate_fft_set(window=window)
              self.p.update(self.dataset.freq_data_list)
              if self.current_view is not 'FFT':
                  self.current_view = 'FFT'
                  self.p.auto_x()
                  self.p.auto_y() 
                  
              xlim = self.p.ax.get_xlim()
              ylim = self.p.ax.get_ylim()
              self.text_axes[2].value = xlim[0]
              self.text_axes[3].value = xlim[1]
              self.text_axes[4].value = ylim[0]
              self.text_axes[5].value = ylim[1]
              self.refresh_buttons()
          
      
      def tf(self,b):
          N_frames = self.N_frames
          window = self.drop_window.value
          if window is 'None':
              window = None
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              self.dataset.calculate_tf_set(window=window,N_frames=N_frames,overlap=self.overlap)
              self.p.update(self.dataset.tf_data_list)
              if self.current_view is not 'TF':
                  self.current_view = 'TF'
                  self.p.auto_x()
                  self.p.auto_y()
              xlim = self.p.ax.get_xlim()
              ylim = self.p.ax.get_ylim()
              self.text_axes[2].value = xlim[0]
              self.text_axes[3].value = xlim[1]
              self.text_axes[4].value = ylim[0]
              self.text_axes[5].value = ylim[1]
              self.refresh_buttons()
      
      def tf_av(self,b):
          window = self.drop_window.value
          if window is 'None':
              window = None
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              self.dataset.calculate_tf_averaged(window=window)
              self.p.update(self.dataset.tf_data_list)
              if self.current_view is not 'TFAV':
                  self.current_view = 'TFAV'
                  self.p.auto_x()
                  self.p.auto_y()
                  
              xlim = self.p.ax.get_xlim()
              ylim = self.p.ax.get_ylim()
              self.text_axes[2].value = xlim[0]
              self.text_axes[3].value = xlim[1]
              self.text_axes[4].value = ylim[0]
              self.text_axes[5].value = ylim[1]
              self.refresh_buttons()
              
      def xiw_fft(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              if self.current_view is 'FFT':
                  s = self.p.get_selected_channels()
                  n_sets,n_chans = np.shape(s)
                  for ns in range(n_sets):
                      newdata = analysis.multiply_by_power_of_iw(self.dataset.freq_data_list[ns],power=1,channel_list=s[ns,:])
                      self.dataset.freq_data_list[ns] = newdata
                  self.p.update(self.dataset.freq_data_list)
                  self.p.auto_y()
                  self.iw_fft_power += 1
                  print('Multiplied by (iw)**{}'.format(self.iw_fft_power))
              else:
                  print('First press <Calc FFT>')
      
      def diw_fft(self,b):
         # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              if self.current_view is 'FFT':
                  s = self.p.get_selected_channels()
                  n_sets,n_chans = np.shape(s)
                  for ns in range(n_sets):
                      newdata = analysis.multiply_by_power_of_iw(self.dataset.freq_data_list[ns],power=-1,channel_list=s[ns,:])
                      self.dataset.freq_data_list[ns] = newdata
                  self.p.update(self.dataset.freq_data_list)
                  self.p.auto_y()
                  self.iw_fft_power -= 1
                  print('Multiplied by (iw)**{}'.format(self.iw_fft_power))
              else:
                  print('First press <Calc FFT>')
                  
      def xiw_tf(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              if (self.current_view is 'TF') or (self.current_view is 'TFAV'):
                  s = self.p.get_selected_channels()
                  n_sets,n_chans = np.shape(s)
                  for ns in range(n_sets):
                      newdata = analysis.multiply_by_power_of_iw(self.dataset.tf_data_list[ns],power=1,channel_list=s[ns,:])
                      self.dataset.tf_data_list[ns] = newdata
                  self.p.update(self.dataset.tf_data_list)
                  self.p.auto_y()
                  self.iw_tf_power += 1
                  print('Multiplied selected channel by (iw)**{}'.format(self.iw_tf_power))
              else:
                  print('First press <Calc TF> or <Calc TF average>')
                  
      def diw_tf(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              if (self.current_view is 'TF') or (self.current_view is 'TFAV'):
                  s = self.p.get_selected_channels()
                  n_sets,n_chans = np.shape(s)
                  for ns in range(n_sets):
                      newdata = analysis.multiply_by_power_of_iw(self.dataset.tf_data_list[ns],power=-1,channel_list=s[ns,:])
                      self.dataset.tf_data_list[ns] = newdata
                  self.p.update(self.dataset.tf_data_list)
                  self.p.auto_y()
                  self.iw_tf_power -= 1
                  print('Multiplied selected channel by (iw)**{}'.format(self.iw_tf_power))
              else:
                  print('First press <Calc TF> or <Calc TF average>')
                  
      def match(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              if (self.current_view is 'TF') or (self.current_view is 'TFAV'):
                  freq_range = self.p.ax.get_xlim()
                  current_calibration_factors = self.dataset.tf_data_list.get_calibration_factors()
                  reference = current_calibration_factors[0][0]
                  factors = analysis.best_match(self.dataset.tf_data_list,freq_range=freq_range,set_ref=0,ch_ref=0)
                  factors = [reference*x for x in factors]
                  self.dataset.tf_data_list.set_calibration_factors_all(factors)
                  self.p.update(self.dataset.tf_data_list)
                  print('scale factors:')
                  print(factors)
                  #self.p.auto_y()
              else:
                  print('First press <View TF> or <Calc TF> or <Calc TF average>')
      
      def save_data(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              print('Saving dataset:')
              print(self.dataset)
              self.dataset.save_data()
              
      
      def save_fig(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              file.save_fig(self.p,figsize=(9,5))
              
      def refresh_buttons(self):
          if len(self.dataset.time_data_list) is 0:
              self.buttons_view[0].button_style =''
          else:
              self.buttons_view[0].button_style ='info'
              
          if len(self.dataset.freq_data_list) is 0:
              self.buttons_view[1].button_style =''
          else:
              self.buttons_view[1].button_style ='info'
              
          if len(self.dataset.tf_data_list) is 0:
              self.buttons_view[2].button_style =''
          else:
              self.buttons_view[2].button_style ='info'
      
  
  
  
  
  #%%
  class InteractiveView():
      def __init__(self):
          
          
          self.dataset = datastructure.DataSet()
          
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out = Output()
          self.out_top = Output()
          
          # Initialise
          self.current_view = 'Time'    
          self.N_frames = 1
          self.overlap = 0.5
          
          # BUTTONS
          items_load = ['Load Data','Reset (deletes loaded data)']
          items_view = ['View Time', 'View FFT', 'View TF']
          items_axes = ['xmin','xmax','ymin','ymax']
          items_save = ['Save Dataset','Save Figure']
          items_legend = ['Left','on/off','Right']
          
          self.buttons_load = [Button(description=i, layout=Layout(width='50%')) for i in items_load]
          self.buttons_load[0].button_style ='primary'
          self.buttons_load[0].style.font_weight = 'bold'
          self.buttons_load[1].button_style ='warning'
          self.buttons_load[1].style.font_weight = 'bold'
  
          
          self.buttons_view = [Button(description=i, layout=Layout(width='99%')) for i in items_view]
          self.buttons_view[0].button_style ='info'
          self.buttons_view[1].button_style ='info'
          self.buttons_view[2].button_style ='info'
          
          self.buttons_match = Button(description='Match Amplitudes', layout=Layout(width='99%'))
          self.buttons_match.button_style = 'info'
          
          
          self.buttons_save = [Button(description=i, layout=Layout(width='50%')) for i in items_save]
          self.buttons_save[0].button_style = 'success'
          self.buttons_save[0].style.font_weight = 'bold'
          self.buttons_save[1].button_style = 'success'
          self.buttons_save[1].style.font_weight = 'bold'
          
          
          self.button_X = Button(description='Auto X', layout=Layout(width='10%'))
          self.button_Y = Button(description='Auto Y', layout=Layout(width='10%'))
          self.button_X.button_style ='success'
          self.button_Y.button_style ='success'
          
          self.buttons_legend = [Button(description=i, layout=Layout(width='16%')) for i in items_legend]
          self.buttons_legend_toggle = ToggleButton(description='Click-and-drag', layout=Layout(width='16%',alignitems='start'))
          
          # TEXT/LABELS/DROPDOWNS
          self.item_axis_label = Label(value="Adjust axes manually:",layout=Layout(width='16%'))
          self.text_axes = [FloatText(value=0,description=i, layout=Layout(width='16%')) for i in items_axes]
          self.text_axes = [self.button_X]+[self.button_Y] + [self.item_axis_label] + self.text_axes
          self.item_legend_label = Label(value="Legend position:",layout=Layout(width='16%'))
          self.item_blank_label = Label(value="",layout=Layout(width='20%'))
          
          group1 = VBox([self.buttons_view[0]],layout=Layout(width='33%'))
          group2 = VBox([self.buttons_view[1]],layout=Layout(width='33%'))
          group3 = VBox([self.buttons_view[2],self.buttons_match],layout=Layout(width='33%'))
          
          # ASSEMBLE
          display(HBox(self.buttons_load))
          self.p = plotting.PlotData()
          display(HBox(self.text_axes))
          display(HBox([self.item_blank_label,self.item_legend_label]+self.buttons_legend+[self.buttons_legend_toggle]))
          display(HBox([group1,group2,group3]))
          display(HBox(self.buttons_save))
          
          
          # Make interactive
          self.text_axes[3].observe(self.xmin, names='value')
          self.text_axes[4].observe(self.xmax, names='value')
          self.text_axes[5].observe(self.ymin, names='value')
          self.text_axes[6].observe(self.ymax, names='value')
          
          self.button_X.on_click(self.auto_x)
          self.button_Y.on_click(self.auto_y)
          
          self.buttons_legend[0].on_click(self.legend_left)
          self.buttons_legend[1].on_click(self.legend_onoff)
          self.buttons_legend[2].on_click(self.legend_right)
          self.buttons_legend_toggle.observe(self.legend_toggle)
          
          self.buttons_load[0].on_click(self.load_data)
          self.buttons_load[1].on_click(self.undo)
          
          self.buttons_view[0].on_click(self.time)
          self.buttons_view[1].on_click(self.fft)
          self.buttons_view[2].on_click(self.tf)
          
          self.buttons_match.on_click(self.match)
          
          self.buttons_save[0].on_click(self.save_data)
          self.buttons_save[1].on_click(self.save_fig)
          
          self.refresh_buttons()
          
          # Put output text at bottom
          display(self.out)
          
      
      def xmin(self,v):
          xmin = self.text_axes[3].value
          xlim = self.p.ax.get_xlim()
          self.p.ax.set_xlim([xmin,xlim[1]])
          
      def xmax(self,v):
          xmax = self.text_axes[4].value
          xlim = self.p.ax.get_xlim()
          self.p.ax.set_xlim([xlim[0],xmax])
          
      def ymin(self,v):
          ymin = self.text_axes[5].value
          ylim = self.p.ax.get_ylim()
          self.p.ax.set_ylim([ymin,ylim[1]])
          
      def ymax(self,v):
          ymax = self.text_axes[6].value
          ylim = self.p.ax.get_ylim()
          self.p.ax.set_ylim([ylim[0],ymax])
          
      def auto_x(self,b):
          self.p.auto_x()
          
      def auto_y(self,b):
          self.p.auto_y()
          
      def legend_left(self,b):
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              self.legend_loc = 'lower left'
              self.p.update_legend(self.legend_loc)
          
      def legend_right(self,b):
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              self.legend_loc = 'lower right'
              self.p.update_legend(self.legend_loc)
              
      def legend_onoff(self,b):
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              visibility = self.p.ax.get_legend().get_visible()
              self.p.ax.get_legend().set_visible(not visibility)
                  
      def legend_toggle(self,b):
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out_top:
              self.p.ax.get_legend().set_visible(True)
              self.p.legend.set_draggable(self.buttons_legend_toggle.value)
          
          
      def load_data(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          self.out_top.clear_output(wait=False)
          with self.out:
              d = file.load_data()
              if d is not None:
                  self.dataset.add_to_dataset(d.time_data_list)
                  self.dataset.add_to_dataset(d.freq_data_list)
                  self.dataset.add_to_dataset(d.tf_data_list)
                  self.dataset.add_to_dataset(d.cross_spec_data_list)
                  self.dataset.add_to_dataset(d.sono_data_list)
              else:
                  print('No data loaded')
              
              if len(self.dataset.time_data_list) is not 0:
                  self.p.update(self.dataset.time_data_list,sets='all',channels='all')
              elif len(self.dataset.freq_data_list) is not 0:
                  self.p.update(self.dataset.freq_data_list,sets='all',channels='all')
              elif len(self.dataset.tf_data_list) is not 0:
                  self.p.update(self.dataset.tf_data_list,sets='all',channels='all')
              else:
                  print('No data to view')
             
              self.refresh_buttons()
          
              self.p.auto_x()
              self.p.auto_y()
              
              xlim = self.p.ax.get_xlim()
              ylim = self.p.ax.get_ylim()
              self.text_axes[3].value = xlim[0]
              self.text_axes[4].value = xlim[1]
              self.text_axes[5].value = ylim[0]
              self.text_axes[6].value = ylim[1]
          
          
      def undo(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.dataset = datastructure.DataSet()
          self.out.clear_output(wait=False)
          with self.out:
              N = len(self.dataset.time_data_list)
              self.p.update(self.dataset.time_data_list,sets=[N-1],channels='all')
          
  
          
      def time(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          with self.out:
              N = len(self.dataset.time_data_list)
              if N is not 0:
                  self.p.update(self.dataset.time_data_list)
                  if self.current_view is not 'Time':
                      self.current_view = 'Time'
                      self.p.auto_x()
                      self.p.auto_y()
  
                  xlim = self.p.ax.get_xlim()
                  ylim = self.p.ax.get_ylim()
                  self.text_axes[3].value = xlim[0]
                  self.text_axes[4].value = xlim[1]
                  self.text_axes[5].value = ylim[0]
                  self.text_axes[6].value = ylim[1]
              else:
                  print('no time data to display')
              
      def fft(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          with self.out:
              N = len(self.dataset.freq_data_list)
              if N is not 0:
                  self.p.update(self.dataset.freq_data_list)
                  if self.current_view is not 'FFT':
                      self.current_view = 'FFT'
                      self.p.auto_x()
                      self.p.auto_y() 
                      
                  xlim = self.p.ax.get_xlim()
                  ylim = self.p.ax.get_ylim()
                  self.text_axes[3].value = xlim[0]
                  self.text_axes[4].value = xlim[1]
                  self.text_axes[5].value = ylim[0]
                  self.text_axes[6].value = ylim[1]
              else:
                  print('no FFT data to display')
      
      def tf(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          with self.out:
              N = len(self.dataset.tf_data_list)
              if N is not 0:
                  self.p.update(self.dataset.tf_data_list)
                  if self.current_view is not 'TF':
                      self.current_view = 'TF'
                      self.p.auto_x()
                      self.p.auto_y()
                  xlim = self.p.ax.get_xlim()
                  ylim = self.p.ax.get_ylim()
                  self.text_axes[3].value = xlim[0]
                  self.text_axes[4].value = xlim[1]
                  self.text_axes[5].value = ylim[0]
                  self.text_axes[6].value = ylim[1]
              else:
                  print('no TF data to display')
                  
      def match(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          with self.out:
              if self.current_view is 'TF':
                  freq_range = self.p.ax.get_xlim()
                  current_calibration_factors = self.dataset.tf_data_list.get_calibration_factors()
                  reference = current_calibration_factors[0][0]
                  factors = analysis.best_match(self.dataset.tf_data_list,freq_range=freq_range,set_ref=0,ch_ref=0)
                  factors = [reference*x for x in factors]
                  self.dataset.tf_data_list.set_calibration_factors_all(factors)
                  self.p.update(self.dataset.tf_data_list)
                  print('scale factors:')
                  print(factors)
                  #self.p.auto_y()
              else:
                  print('First press <View TF>')
              
      
      
      def save_data(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          with self.out:
              print('Saving dataset:')
              print(self.dataset)
              self.dataset.save_data()
              
      
      def save_fig(self,b):
          # the 'out' construction is to refresh the text output at each update 
          # to stop text building up in the widget display
          self.out.clear_output(wait=False)
          with self.out:
              file.save_fig(self.p,figsize=(9,5))
      
      def refresh_buttons(self):
          if len(self.dataset.time_data_list) is 0:
              self.buttons_view[0].button_style =''
          else:
              self.buttons_view[0].button_style ='info'
              
          if len(self.dataset.freq_data_list) is 0:
              self.buttons_view[1].button_style =''
          else:
              self.buttons_view[1].button_style ='info'
              
          if len(self.dataset.tf_data_list) is 0:
              self.buttons_view[2].button_style =''
          else:
              self.buttons_view[2].button_style ='info'
                  