コード例 #1
0
class Spectroscopy:
    """ Base class for spectroscopy experiments """

    def __init__(self, master, devman):

        self.master = master
        self.dm = devman
        self.id = 'spec'
        self.header = 'Wavelength (nm)\tCh-1\tCh-2'
        self.extension = 'txt'

        # Pre-load the batch, witout creating any interface
        self.batch = Batch(self.master, self.dm, fileheader=self.header)

        # Create the main variables of the class
        self.create_variables()

        # Create the interface
        self.create_interface()
        self.plot_format = {'ratios' : (3, 1),
                            'xlabel' : 'Wavelength (nm)',
                            'Ch1_ylabel' : 'Ch-1',
                            'Ch2_ylabel' : 'Ch-2',
                            'Ch1_scale' : 'linear',
                            'Ch2_scale' : 'linear'}

        # We load the dummy devices by default
        self.fill_devices()

    def quit(self):
        """ Safe closing of the devices. The devices must be closed by the Device Manager, not directly,
        so they are registered as "free".
        """

        if self.monochromator is not None:
            self.dm.close_device(self.monochromator)
        if self.adquisition is not None:
            self.dm.close_device(self.adquisition)

        self.batch.quit()

    def create_variables(self):

        # Data variables
        self.record = None
        self.background = None

        # Hardware variables
        self.monochromator = None
        self.adquisition = None

        # Adquisition variables
        self.integration_time = 300
        self.waiting_time = 100
        self.stop = True

    def create_interface(self):

        # Add elements to the menu bar
        self.create_menu_bar()

        # Creates the spectroscopy frame
        self.spectroscopy_frame = ttk.Frame(master=self.master.control_frame)
        self.spectroscopy_frame.grid(column=0, row=0, sticky=(tk.EW))
        self.spectroscopy_frame.columnconfigure(0, weight=1)

        # Hardware widgets
        hardware_frame = ttk.Labelframe(self.spectroscopy_frame, text='Selected hardware:', padding=(0, 5, 0, 15))
        hardware_frame.columnconfigure(0, weight=1)
        hardware_frame.grid(column=0, row=0, sticky=(tk.EW))

        self.mono_var = tk.StringVar()
        self.adq_var = tk.StringVar()
        self.monochromator_box = ttk.Combobox(master=hardware_frame, textvariable=self.mono_var, state="readonly")
        self.adquisition_box = ttk.Combobox(master=hardware_frame, textvariable=self.adq_var, state="readonly")

        self.monochromator_box.bind('<<ComboboxSelected>>', self.select_monochromator)
        self.adquisition_box.bind('<<ComboboxSelected>>', self.select_adquisition)

        self.monochromator_box.grid(column=0, row=0, sticky=(tk.EW))
        self.adquisition_box.grid(column=0, row=1, sticky=(tk.EW))

        # Set widgets ---------------------------------
        set_frame = ttk.Labelframe(self.spectroscopy_frame, text='Set:', padding=(0, 5, 0, 15))
        set_frame.columnconfigure(1, weight=1)

        self.GoTo_button = ttk.Button(master=set_frame, text='GoTo', command=self.goto)
        self.GoTo_entry = ttk.Entry(master=set_frame, width=10)
        self.GoTo_entry.insert(0, '700.0')
        self.integration_time_button = ttk.Button(master=set_frame, text='Integration time (ms)',
                                                  command=self.update_integration_time)
        self.integration_time_entry = ttk.Entry(master=set_frame, width=10)
        self.integration_time_entry.insert(0, '300')
        self.waiting_time_button = ttk.Button(master=set_frame, text='Waiting time (ms)',
                                              command=self.update_waiting_time)
        self.waiting_time_entry = ttk.Entry(master=set_frame, width=10)
        self.waiting_time_entry.insert(0, '100')

        set_frame.grid(column=0, row=1, sticky=(tk.EW))
        self.GoTo_button.grid(column=0, row=0, sticky=(tk.EW))
        self.GoTo_entry.grid(column=1, row=0, sticky=(tk.EW))
        self.integration_time_button.grid(column=0, row=2, sticky=(tk.EW))
        self.integration_time_entry.grid(column=1, row=2, sticky=(tk.EW))
        self.waiting_time_button.grid(column=0, row=3, sticky=(tk.EW))
        self.waiting_time_entry.grid(column=1, row=3, sticky=(tk.EW))

        # Live adquisition widgets
        live_frame = ttk.Labelframe(self.spectroscopy_frame, text='Live:', padding=(0, 5, 0, 15))
        live_frame.columnconfigure(1, weight=1)
        live_frame.columnconfigure(2, weight=1)

        self.window_live_lbl = ttk.Label(master=live_frame, text="Window (points):")
        self.window_live_entry = ttk.Entry(master=live_frame, width=10)
        self.window_live_entry.insert(0, '100')

        self.record_live_button = ttk.Button(master=live_frame, text='Record', command=self.record_live)
        self.pause_live_button = ttk.Button(master=live_frame, text='Pause', state=tk.DISABLED, command=self.pause_live)

        live_frame.grid(column=0, row=2, sticky=(tk.EW))
        self.window_live_lbl.grid(column=0, row=0, sticky=(tk.EW))
        self.window_live_entry.grid(column=1, row=0, columnspan=2, sticky=(tk.EW))
        self.record_live_button.grid(column=1, row=3, sticky=(tk.EW))
        self.pause_live_button.grid(column=2, row=3, sticky=(tk.EW))

        # Scan widgets ---------------------------------
        scan_frame = ttk.Labelframe(self.spectroscopy_frame, text='Scan:', padding=(0, 5, 0, 15))
        scan_frame.columnconfigure(0, weight=1)

        Start_lbl = ttk.Label(master=scan_frame, text="Start (nm):")
        self.Start_entry = ttk.Entry(master=scan_frame)
        self.Start_entry.insert(0, '700.0')
        Stop_lbl = ttk.Label(master=scan_frame, text="Stop (nm):")
        self.Stop_entry = ttk.Entry(master=scan_frame)
        self.Stop_entry.insert(0, '900.0')
        Step_lbl = ttk.Label(master=scan_frame, text="Step (nm):")
        self.Step_entry = ttk.Entry(master=scan_frame)
        self.Step_entry.insert(0, '5.0')

        self.scan_button = ttk.Button(master=scan_frame, text='Run', command=self.start_stop_scan, width=7)
        self.pause_button = ttk.Button(master=scan_frame, text='Pause', state=tk.DISABLED, command=self.pause_scan,
                                       width=7)

        scan_frame.grid(column=0, row=3, sticky=(tk.EW))
        Start_lbl.grid(column=0, row=0, sticky=(tk.EW))
        self.Start_entry.grid(column=1, row=0, columnspan=2, sticky=(tk.EW))
        Stop_lbl.grid(column=0, row=1, sticky=(tk.EW))
        self.Stop_entry.grid(column=1, row=1, columnspan=2, sticky=(tk.EW))
        Step_lbl.grid(column=0, row=2, sticky=(tk.EW))
        self.Step_entry.grid(column=1, row=2, columnspan=2, sticky=(tk.EW))
        self.scan_button.grid(column=1, row=3, sticky=(tk.EW))
        self.pause_button.grid(column=2, row=3, sticky=(tk.EW))

        # Background widgets
        self.background_frame = ttk.Labelframe(self.spectroscopy_frame, text='Background:', padding=(0, 5, 0, 15))
        self.background_frame.columnconfigure(0, weight=1)
        self.background_frame.columnconfigure(1, weight=1)
        self.background_button = ttk.Button(master=self.background_frame, text='Get', command=self.get_background)
        self.clear_background_button = ttk.Button(master=self.background_frame, text='Clear',
                                                  command=self.clear_background)

        self.background_frame.grid(column=0, row=4, sticky=(tk.NSEW))
        self.background_button.grid(column=0, row=0, sticky=(tk.EW, tk.S))
        self.clear_background_button.grid(column=1, row=0, sticky=(tk.EW, tk.S))

    def create_menu_bar(self):
        """ Add elememnts to the master menubar
        """

        # Hardware menu
        self.master.menu_hardware.add_command(label='Monochromator', command=lambda: self.monochromator.interface(self.master))
        self.master.menu_hardware.entryconfig("Monochromator", state="disabled")
        self.master.menu_hardware.add_command(label='Adquisition', command=lambda: self.adquisition.interface(self.master))
        self.master.menu_hardware.entryconfig("Adquisition", state="disabled")

        # Batch menu
        self.master.menu_batch.add_command(label='Disable', command=self.batch.disable)
        self.master.menu_batch.add_command(label='IV', command=lambda: self.new_batch('IV'))
        self.master.menu_batch.add_command(label='Temperature', command=lambda: self.new_batch('Temperature'))
        self.master.menu_batch.add_command(label='Time', command=lambda: self.new_batch('Time'))

    def new_batch(self, batch_mode):
        """ Shows current batch window or, if a different batch is chosen, destroys the old one and creates a new one
        for the new batch mpde

        :param batch_mode: the selected type of batch
        :return: None
        """
        if self.batch.mode == batch_mode:
            self.batch.show()
        else:
            self.batch.quit()
            self.batch = Batch(self.master, self.dm, mode=batch_mode)

    def fill_devices(self):
        """ Fills the device selectors with the corresponding type of devices

        :return:
        """

        self.monochromator_box['values'] = self.dm.get_devices(['Monochromator'])
        self.monochromator_box.current(0)
        self.adquisition_box['values'] = self.dm.get_devices(['Lock-In', 'Spectrometer'])
        self.adquisition_box.current(0)

        self.select_monochromator()
        self.select_adquisition()

    def select_monochromator(self, *args):

        if self.monochromator is not None:
            self.dm.close_device(self.monochromator)

        dev_name = self.mono_var.get()
        self.monochromator = self.dm.open_device(dev_name)

        if self.monochromator is None:
            self.monochromator_box.current(0)
            self.monochromator = self.dm.open_device(self.mono_var.get())

        elif self.dm.current_config[dev_name]['Type'] == 'Monochromator':
            self.move = self.monochromator.move

        else:
            self.monochromator_box.current(0)
            self.monochromator = self.dm.open_device(self.mono_var.get())

        # If the device has an interface to set options, we link it to the entry in the menu
        interface = getattr(self.monochromator, "interface", None)
        if callable(interface):
            self.master.menu_hardware.entryconfig("Monochromator", state="normal")
        else:
            self.master.menu_hardware.entryconfig("Monochromator", state="disabled")

    def select_adquisition(self, *args):
        """ When the adquisition selector changes, this function updates some variables and the graphical interface
        to adapt it to the selected device.

        :param args: Dummy variable that does nothing but must exist (?)
        :return: None
        """

        if self.adquisition is not None:
            self.dm.close_device(self.adquisition)

        dev_name = self.adq_var.get()
        self.adquisition = self.dm.open_device(dev_name)

        if self.adquisition is None:
            self.adquisition_box.current(0)
            self.adquisition = self.dm.open_device(self.adq_var.get())

        elif self.dm.current_config[dev_name]['Type'] == 'Spectrometer':
            self.move = self.null
            self.prepare_scan = self.prepare_scan_spectrometer
            self.get_next_datapoint = self.mode_spectrometer
            self.start_live = self.prepare_live_spectrometer
            self.live = self.live_spectrometer
            self.background_frame.grid(column=0, row=4, sticky=(tk.NSEW))
            self.window_live_lbl.grid_forget()
            self.window_live_entry.grid_forget()
            self.monochromator_box['state'] = 'disabled'
            self.Step_entry['state'] = 'disabled'
            self.GoTo_button['state'] = 'disabled'
            self.GoTo_entry['state'] = 'disabled'

        elif self.dm.current_config[dev_name]['Type'] in ['Lock-In', 'Multimeter']:
            self.move = self.monochromator.move
            self.prepare_scan = self.prepare_scan_lockin
            self.get_next_datapoint = self.mode_lockin
            self.start_live = self.prepare_live_lockin
            self.live = self.live_lockin
            self.background_frame.grid_forget()
            self.window_live_lbl.grid(column=0, row=0, sticky=(tk.EW))
            self.window_live_entry.grid(column=1, row=0, columnspan=2, sticky=(tk.EW))
            self.monochromator_box['state'] = 'normal'
            self.Step_entry['state'] = 'normal'
            self.GoTo_button['state'] = 'normal'
            self.GoTo_entry['state'] = 'normal'

        else:
            self.adquisition_box.current(0)
            self.adquisition = self.dm.open_device(self.adq_var.get())

        interface = getattr(self.adquisition, "interface", None)
        if callable(interface):
            self.master.menu_hardware.entryconfig("Adquisition", state="normal")
        else:
            self.master.menu_hardware.entryconfig("Adquisition", state="disabled")

    def null(self, *args, **kwargs):
        """ Empty function that does nothing

        :return: None
        """
        pass

    def start_stop_scan(self):
        """ Starts and stops an scan

        :return: None
        """
        if self.stop:
            self.prepare_scan()
        else:
            self.stop = True
            self.finish_scan()

    def pause_scan(self):
        """ Pauses an scan or resumes the adquisition

        :return: None
        """
        self.stop = not self.stop

        if self.stop:
            self.pause_button['text'] = 'Resume'
        else:
            self.pause_button['text'] = 'Pause'

        self.get_next_datapoint()

    def prepare_scan_lockin(self):
        """ Any scan is divided in three stages:
        1) Prepare the conditions of the scan (this function), getting starting point, integration time and creating all relevant variables.
        2) Runing the scan, performed by a recursive function  "mode_spectrometer" or "mode_lockin"
        3) Finish the scan, where we update some variables and save the data.

        :return: None
        """
        self.update_integration_time()
        self.update_waiting_time()

        # Get the scan conditions
        self.start_wl = max(float(self.Start_entry.get()), 250)
        self.stop_wl = max(min(float(self.Stop_entry.get()), 3000), self.start_wl + 1)
        step = min(max(float(self.Step_entry.get()), self.adquisition.min_wavelength), self.stop_wl - self.start_wl)

        # # If we are in a batch, we proceed to the next point
        if self.batch.ready:
            self.batch.batch_proceed()

        self.move(self.start_wl, speed='Fast')
        print('Starting scan...')

        # Create the record array
        self.size = int(np.ceil((self.stop_wl - self.start_wl + 0.5 * step) / step))
        self.num = self.size
        self.record = np.zeros((self.size, 3))
        self.record[:, 0] = np.arange(self.start_wl, self.stop_wl + 0.5 * step, step)
        self.record[:, 1] = self.record[:, 1] * np.NaN
        self.record[:, 2] = self.record[:, 1] * np.NaN

        self.master.prepare_meas(self.record)

        self.i = 0

        self.scan_running()

        self.mode_lockin()

    def mode_lockin(self):
        """ Gets the next data point in a scan. This function depends on the adquisition device

        :return: None
        """

        if not self.stop:
            data = self.adquisition.measure()
            self.record[self.i, 1] = data[0]
            self.record[self.i, 2] = data[1]

            self.master.update_plot(self.record)

            if self.i < self.num - 1:
                self.i += 1
                self.move(self.record[self.i, 0], speed='Fast')
                self.master.window.after(int(self.integration_time + self.waiting_time), self.mode_lockin)

            else:
                self.finish_scan()

    def prepare_scan_spectrometer(self):
        """ Any scan is divided in three stages:
        1) Prepare the conditions of the scan (this function), getting starting point, integration time and creating all relevant variables.
        2) Runing the scan, performed by a recursive function "mode_spectrometer" or "mode_lockin"
        3) Finish the scan, where we update some variables and save the data.

        :return: None
        """
        self.update_integration_time()
        self.update_waiting_time()

        # We check the background is not none and offer to update it
        if self.background is None:
            meas_bg = messagebox.askyesno(message='There is no background spectrum for this integration time!',
                                              detail='Do you want to measure it now?', icon='question',
                                              title='Measure background?')

            self.get_background(meas_bg)

        # # If we are in a batch, we proceed to the next point
        if self.batch.ready:
            self.batch.batch_proceed()

        # If the integration time is too long, we have to split the adquisition in several steps,
        # otherwise the spectrometer hangs
        self.num = int(np.ceil(self.integration_time / self.adquisition.max_integration_time))

        # Here we select the wavelength range we want to record and re-shape the record array
        # Get the scan conditions
        self.start_wl = max(float(self.Start_entry.get()), 300)
        self.stop_wl = max(min(float(self.Stop_entry.get()), 2000), self.start_wl + 1)
        wl = self.adquisition.measure()[0]
        self.idx = np.where((self.start_wl <= wl) & (wl <= self.stop_wl))
        self.size = len(self.idx[0])

        # Create the record array
        self.record = np.zeros((self.size, 3))
        self.record[:, 0] = wl[self.idx]
        self.record[:, 2] = self.background[self.idx]

        self.master.prepare_meas(self.record)

        self.i = 0

        self.scan_running()

        self.mode_spectrometer()

    def mode_spectrometer(self):
        """ Gets the whole spectrum at once recorded by the spectrometer in the range selected

        :return: None
        """

        if not self.stop:
            data = self.adquisition.measure()
            intensity = data[1][self.idx] - self.background[self.idx]
            self.record[:, 1] = (intensity + self.i * self.record[:, 1]) / (self.i + 1.)

            self.master.update_plot(self.record)

            if self.i < self.num - 1:
                self.i = self.i + 1
                self.master.window.after(int(self.integration_time / self.num), self.mode_spectrometer)

            else:
                self.finish_scan()

    def finish_scan(self):
        """ Finish the scan, updating some global variables, saving the data in the temp file and offering to save the
        data somewhere else.

        :return: None
        """

        if self.batch.ready:
            self.master.finish_meas(self.record, finish=False)
            self.batch.batch_wrapup(self.record)
        else:
            self.master.finish_meas(self.record, finish=True)

        if self.stop or not self.batch.ready:
            # We reduce the number of counts in the batch to resume at the unfinished point if necessary
            # In other words, it repeats the last, un-finished measurement
            self.batch.count = max(self.batch.count - 1, 0)
            self.scan_stopped()

        else:
            self.prepare_scan()

    def scan_running(self):
        """ Updates the graphical interface, disable the buttoms that must be disabled during the measurement.

        :return: None
        """
        self.scan_button['text'] = 'Stop'
        self.pause_button['state'] = 'enabled'
        self.record_live_button['state'] = 'disabled'
        self.GoTo_button['state'] = 'disabled'
        self.integration_time_button['state'] = 'disabled'
        self.waiting_time_button['state'] = 'disabled'
        self.background_button['state'] = 'disabled'
        self.clear_background_button['state'] = 'disabled'
        self.stop = False

    def scan_stopped(self):
        """ Returns the graphical interface to normal once the measurement has finished.

        :return: None
        """
        self.scan_button['text'] = 'Run'
        self.pause_button['state'] = 'disabled'
        self.record_live_button['state'] = 'enabled'
        self.GoTo_button['state'] = 'enabled'
        self.integration_time_button['state'] = 'enabled'
        self.waiting_time_button['state'] = 'enabled'
        self.background_button['state'] = 'enabled'
        self.clear_background_button['state'] = 'enabled'
        self.stop = True

    def get_background(self, meas_bg=True):
        """ Gets the background when using the spectrometer, if requested.

        :parameter: meas_bg     True or False: wether to measure a background or just produce a bg with zeros
        :return: None
        """

        if meas_bg:
            self.update_integration_time()
            self.background = self.adquisition.measure()[1]
            messagebox.showinfo(message='Background taken!', detail='Press OK to continue.', title='Background taken!')
        else:
            self.background = self.adquisition.measure()[1]*0.0

    def clear_background(self):
        """ Clears the background when using the spectrometer.

        :return: None
        """
        self.background = None

    def record_live(self):
        """ Starts and stops a live recording.

        :return: None
        """
        if self.stop:
            self.stop = False
            self.record_live_button['text'] = 'Stop'
            self.pause_live_button['state'] = 'enabled'
            self.scan_button['state'] = 'disabled'
            self.background_button['state'] = 'disabled'
            self.clear_background_button['state'] = 'disabled'
            self.start_live()
        else:
            self.record_live_button['text'] = 'Record'
            self.pause_live_button['state'] = 'disabled'
            self.scan_button['state'] = 'enabled'
            self.background_button['state'] = 'enabled'
            self.clear_background_button['state'] = 'enabled'
            self.stop = True
            self.finish_live()

    def pause_live(self):
        """ Pauses a live recording or resumes the adquisition
        """
        self.stop = not self.stop

        if self.stop:
            self.pause_live_button['text'] = 'Resume'
        else:
            self.pause_live_button['text'] = 'Pause'

        self.live()

    def prepare_live_lockin(self):
        """ Prepares the lock-in live adquisition and prepare some variables
        """
        self.goto()
        self.update_integration_time()

        self.window_points = int(self.window_live_entry.get())

        self.live_data = np.zeros((self.window_points, 3))
        self.live_data[:, 0] = np.arange(self.window_points)

        # Removes all plots, but not the data, and change the horizontal axis conditions
        self.master.clear_plot(xtitle='Time', ticks='off')
        self.master.prepare_meas(self.live_data)

        self.live_lockin()

    def live_lockin(self):
        """ Runs the live lock-in adquisition
        """
        if not self.stop:
            self.live_data[:-1, 1] = self.live_data[1:, 1]
            self.live_data[:-1, 2] = self.live_data[1:, 2]

            self.live_data[-1, 1], self.live_data[-1, 2] = self.adquisition.measure()

            self.master.update_plot(self.live_data)

            self.master.window.after(self.integration_time, self.live_lockin)

    def prepare_live_spectrometer(self):
        """ Prepares the spectrometer live adquisition and prepare some variables
        """
        self.update_integration_time()

        if self.integration_time > self.adquisition.max_integration_time:
            self.integration_time = int(self.adquisition.max_integration_time)
            self.adquisition.update_integration_time(self.integration_time)

        data0, data1 = self.adquisition.measure()

        self.live_data = np.zeros((len(data0), 3))
        self.live_data[:, 0] = data0
        self.live_data[:, 1] = data1

        # Removes all plots, but not the data, and change the horizontal axis conditions
        self.master.clear_plot(xtitle='Wavelength (nm)', ticks='on')
        self.master.prepare_meas(self.live_data)

        self.live_spectrometer()

    def live_spectrometer(self):
        """ Runs the live spectrometer adquisition
        """
        if not self.stop:
            data0, data1 = self.adquisition.measure()
            self.live_data[:, 1] = data1

            self.master.update_plot(self.live_data)

            self.master.window.after(self.integration_time, self.live_spectrometer)

    def finish_live(self):
        """ Finish the live adquisition, returning the front end to the scan mode
        """
        self.master.replot_data(xtitle='Wavelength (nm)', ticks='on')

    def update_integration_time(self):
        """ Updates the integration time
        """
        old_integration_time = self.integration_time
        self.integration_time = int(self.integration_time_entry.get())

        if old_integration_time != self.integration_time:
            self.clear_background()
            self.integration_time = self.adquisition.update_integration_time(self.integration_time)
            self.integration_time_entry.delete(0, tk.END)
            self.integration_time_entry.insert(0, '%i' % self.integration_time)

    def update_waiting_time(self):
        """ Updates the waiting time
        """
        self.waiting_time = int(self.waiting_time_entry.get())

    def goto(self):
        """ Go to the specified wavelength
        """
        wl = float(self.GoTo_entry.get())
        self.move(wl, speed='Fast')
        print('Done! Wavelength = {} nm'.format(wl))
コード例 #2
0
class Photoreflectance:
    """ Base class for photoreflectance experiments """
    def __init__(self, master, devman):

        self.master = master
        self.dm = devman
        self.id = 'PR'

        # Create the main variables of the class
        self.create_variables()

        self.plot_format = {
            'ratios': (1, 1),
            'xlabel': 'Wavelength (nm)',
            'x_scale': 'linear',
            'Ch1_ylabel': 'Xsignal/Rbaseline',
            'Ch2_ylabel': 'Rbaseline',
            'Ch1_scale': 'linear',
            'Ch2_scale': 'linear'
        }
        self.header = ''
        self.header.encode('utf-8')
        self.extension = 'txt'

        # Pre-load the batch, witout creating any interface
        self.batch = Batch(self.master, self.dm, fileheader=self.header)

        # Create the interface
        self.create_interface()

        # We load the dummy devices by default
        self.fill_devices()

    def quit(self):
        """ Safe closing of the devices. The devices must be closed by the Device Manager, not directly,
        so they are registered as "free".
        """

        if self.monochromator is not None:
            self.monochromator.close()  ## Destroys the system model
            self.dm.close_device(self.monochromator)
        if self.acquisition is not None:
            self.acquisition.close()  ## Unsubscribes from the demodulators
            self.dm.close_device(self.acquisition)

        self.batch.quit()

    def create_variables(self):

        # Data variables
        self.record = None
        self.background = None
        self.plotdata = None  ## Transformed data to be plotted

        # Hardware variables
        self.monochromator = None
        self.acquisition = None

        # acquisition variables
        self.integration_time = 100
        self.waiting_time = 100
        self.stop = True

    def create_interface(self):

        # Add elements to the menu bar
        self.create_menu_bar()

        # Creates the photoreflectance frame
        self.spectroscopy_frame = ttk.Frame(master=self.master.control_frame)
        self.spectroscopy_frame.grid(column=0, row=0, sticky=(tk.EW))
        self.spectroscopy_frame.columnconfigure(0, weight=1)

        # Hardware widgets
        hardware_frame = ttk.Labelframe(self.spectroscopy_frame,
                                        text='Selected hardware:',
                                        padding=(0, 5, 0, 15))
        hardware_frame.columnconfigure(0, weight=1)
        hardware_frame.grid(column=0, row=0, sticky=(tk.EW))

        self.mono_var = tk.StringVar()
        self.acq_var = tk.StringVar()
        self.monochromator_box = ttk.Combobox(master=hardware_frame,
                                              textvariable=self.mono_var,
                                              state="readonly")
        self.acquisition_box = ttk.Combobox(master=hardware_frame,
                                            textvariable=self.acq_var,
                                            state="readonly")

        self.monochromator_box.bind('<<ComboboxSelected>>',
                                    self.select_monochromator)
        self.acquisition_box.bind('<<ComboboxSelected>>',
                                  self.select_acquisition)

        self.monochromator_box.grid(column=0, row=0, sticky=(tk.EW))
        self.acquisition_box.grid(column=0, row=1, sticky=(tk.EW))

        # Set widgets ---------------------------------
        set_frame = ttk.Labelframe(self.spectroscopy_frame,
                                   text='Set:',
                                   padding=(0, 5, 0, 15))
        set_frame.columnconfigure(1, weight=1)

        self.GoTo_button = ttk.Button(master=set_frame,
                                      text='GoTo',
                                      command=self.goto)
        self.GoTo_entry = ttk.Entry(master=set_frame, width=10)
        self.GoTo_entry.insert(0, '700.0')
        self.integration_time_button = ttk.Button(
            master=set_frame,
            text='Integration time (ms)',
            command=self.update_integration_time)
        self.integration_time_entry = ttk.Entry(master=set_frame, width=10)
        self.integration_time_entry.insert(0, '100')
        self.waiting_time_button = ttk.Button(master=set_frame,
                                              text='Waiting time (ms)',
                                              command=self.update_waiting_time)
        self.waiting_time_entry = ttk.Entry(master=set_frame, width=10)
        self.waiting_time_entry.insert(0, '100')

        set_frame.grid(column=0, row=1, sticky=(tk.EW))
        self.GoTo_button.grid(column=0, row=0, sticky=(tk.EW))
        self.GoTo_entry.grid(column=1, row=0, sticky=(tk.EW))
        self.integration_time_button.grid(column=0, row=2, sticky=(tk.EW))
        self.integration_time_entry.grid(column=1, row=2, sticky=(tk.EW))
        self.waiting_time_button.grid(column=0, row=3, sticky=(tk.EW))
        self.waiting_time_entry.grid(column=1, row=3, sticky=(tk.EW))

        # Live acquisition widgets
        live_frame = ttk.Labelframe(self.spectroscopy_frame,
                                    text='Live:',
                                    padding=(0, 5, 0, 15))
        live_frame.columnconfigure(1, weight=1)
        live_frame.columnconfigure(2, weight=1)

        self.window_live_lbl = ttk.Label(master=live_frame,
                                         text="Window (points):")
        self.window_live_entry = ttk.Entry(master=live_frame, width=10)
        self.window_live_entry.insert(0, '100')

        self.record_live_button = ttk.Button(master=live_frame,
                                             text='Record',
                                             command=self.record_live)
        self.pause_live_button = ttk.Button(master=live_frame,
                                            text='Pause',
                                            state=tk.DISABLED,
                                            command=self.pause_live)

        live_frame.grid(column=0, row=2, sticky=(tk.EW))
        self.window_live_lbl.grid(column=0, row=0, sticky=(tk.EW))
        self.window_live_entry.grid(column=1,
                                    row=0,
                                    columnspan=2,
                                    sticky=(tk.EW))
        self.record_live_button.grid(column=1, row=3, sticky=(tk.EW))
        self.pause_live_button.grid(column=2, row=3, sticky=(tk.EW))

        # Scan widgets ---------------------------------
        scan_frame = ttk.Labelframe(self.spectroscopy_frame,
                                    text='Scan:',
                                    padding=(0, 5, 0, 15))
        scan_frame.columnconfigure(0, weight=1)

        Start_lbl = ttk.Label(master=scan_frame, text="Start (nm):")
        self.Start_entry = ttk.Entry(master=scan_frame)
        self.Start_entry.insert(0, '700.0')
        Stop_lbl = ttk.Label(master=scan_frame, text="Stop (nm):")
        self.Stop_entry = ttk.Entry(master=scan_frame)
        self.Stop_entry.insert(0, '900.0')
        Step_lbl = ttk.Label(master=scan_frame, text="Step (nm):")
        self.Step_entry = ttk.Entry(master=scan_frame)
        self.Step_entry.insert(0, '5.0')

        self.scan_button = ttk.Button(master=scan_frame,
                                      text='Run',
                                      command=self.start_stop_scan,
                                      width=7)
        self.pause_button = ttk.Button(master=scan_frame,
                                       text='Pause',
                                       state=tk.DISABLED,
                                       command=self.pause_scan,
                                       width=7)

        scan_frame.grid(column=0, row=3, sticky=(tk.EW))
        Start_lbl.grid(column=0, row=0, sticky=(tk.EW))
        self.Start_entry.grid(column=1, row=0, columnspan=2, sticky=(tk.EW))
        Stop_lbl.grid(column=0, row=1, sticky=(tk.EW))
        self.Stop_entry.grid(column=1, row=1, columnspan=2, sticky=(tk.EW))
        Step_lbl.grid(column=0, row=2, sticky=(tk.EW))
        self.Step_entry.grid(column=1, row=2, columnspan=2, sticky=(tk.EW))
        self.scan_button.grid(column=1, row=3, sticky=(tk.EW))
        self.pause_button.grid(column=2, row=3, sticky=(tk.EW))

        # Background widgets---------------------------------------------
        self.background_frame = ttk.Labelframe(self.spectroscopy_frame,
                                               text='Background:',
                                               padding=(0, 5, 0, 15))
        self.background_frame.columnconfigure(0, weight=1)
        self.background_frame.columnconfigure(1, weight=1)
        self.background_button = ttk.Button(master=self.background_frame,
                                            text='Get',
                                            command=self.get_background)
        self.clear_background_button = ttk.Button(
            master=self.background_frame,
            text='Clear',
            command=self.clear_background)

        self.background_frame.grid(column=0, row=4, sticky=(tk.NSEW))
        self.background_button.grid(column=0, row=0, sticky=(tk.EW, tk.S))
        self.clear_background_button.grid(column=1,
                                          row=0,
                                          sticky=(tk.EW, tk.S))

        # Ch1 setup widgets --------------------------------
        self.Ch1_frame = ttk.Labelframe(self.spectroscopy_frame,
                                        text='Ch1 setup:',
                                        padding=(0, 5, 0, 15))
        self.Ch1_frame.grid(column=0, row=5, sticky=(tk.EW))
        self.Ch1_frame.columnconfigure(0, weight=1)
        self.Ch1_frame.columnconfigure(1, weight=1)
        self.Ch1_param1_var = tk.StringVar()
        self.Ch1_param1_var.set('Xsignal')
        self.Ch1_param2_var = tk.StringVar()
        self.Ch1_param2_var.set('Rbaseline')
        self.numer_label = ttk.Label(self.Ch1_frame, text='Numerator: ')
        self.numer_label.grid(column=0, row=0, sticky=(tk.E, tk.W, tk.S))
        ttk.Radiobutton(self.Ch1_frame,
                        text="X signal",
                        variable=self.Ch1_param1_var,
                        value='Xsignal',
                        command=self.channel_param).grid(column=1,
                                                         row=0,
                                                         sticky=(tk.E, tk.W,
                                                                 tk.S))
        ttk.Radiobutton(self.Ch1_frame,
                        text="R signal",
                        variable=self.Ch1_param1_var,
                        value='Rsignal',
                        command=self.channel_param).grid(column=2,
                                                         row=0,
                                                         sticky=(tk.E, tk.W,
                                                                 tk.S))
        self.denom_label = ttk.Label(self.Ch1_frame, text='Denominator: ')
        self.denom_label.grid(column=0, row=1, sticky=(tk.E, tk.W, tk.S))
        ttk.Radiobutton(self.Ch1_frame,
                        text="R baseline",
                        variable=self.Ch1_param2_var,
                        value='Rbaseline',
                        command=self.channel_param).grid(column=1,
                                                         row=1,
                                                         sticky=(tk.E, tk.W,
                                                                 tk.S))
        ttk.Radiobutton(self.Ch1_frame,
                        text="No denominator",
                        variable=self.Ch1_param2_var,
                        value='ND',
                        command=self.channel_param).grid(column=2,
                                                         row=1,
                                                         sticky=(tk.E, tk.W,
                                                                 tk.S))

        # Ch2 setup widgets --------------------------------
        self.Ch2_frame = ttk.Labelframe(self.spectroscopy_frame,
                                        text='Ch2 setup:',
                                        padding=(0, 5, 0, 15))
        self.Ch2_frame.grid(column=0, row=6, sticky=(tk.EW))
        self.Ch2_frame.columnconfigure(0, weight=1)
        self.Ch2_frame.columnconfigure(1, weight=1)
        self.Ch2_param_var = tk.StringVar()
        self.Ch2_param_var.set('Rbaseline')
        ttk.Radiobutton(self.Ch2_frame,
                        text="R baseline",
                        variable=self.Ch2_param_var,
                        value='Rbaseline',
                        command=self.channel_param).grid(column=0,
                                                         row=1,
                                                         sticky=(tk.E, tk.W,
                                                                 tk.S))

    def update_header(self):
        col0 = self.plot_format['xlabel']
        col1 = 'Xc'
        col2 = 'Yc'
        col3 = 'Xsum'
        col4 = 'Ysum'
        col5 = 'Xdif'
        col6 = 'Ydif'

        self.header = str(col0) + '\t' + str(col1) + '\t' + str(
            col2) + '\t' + str(col3) + '\t' + str(col4) + '\t' + str(
                col5) + '\t' + str(col6)
        self.header.encode('utf-8')

    def create_menu_bar(self):
        """ Add elememnts to the master menubar
        """

        # Hardware menu
        self.master.menu_hardware.add_command(
            label='Monochromator',
            command=lambda: self.monochromator.interface(self.master))
        self.master.menu_hardware.entryconfig("Monochromator",
                                              state="disabled")
        self.master.menu_hardware.add_command(
            label='acquisition',
            command=lambda: self.acquisition.interface(self.master))
        self.master.menu_hardware.entryconfig("acquisition", state="disabled")

        # Batch menu
        self.master.menu_batch.add_command(label='Disable',
                                           command=self.batch.disable)
        self.master.menu_batch.add_command(
            label='IV', command=lambda: self.new_batch('IV'))
        self.master.menu_batch.add_command(
            label='Temperature', command=lambda: self.new_batch('Temperature'))
        self.master.menu_batch.add_command(
            label='Time', command=lambda: self.new_batch('Time'))

    def new_batch(self, batch_mode):
        """ Shows current batch window or, if a different batch is chosen, destroys the old one and creates a new one
        for the new batch mpde

        :param batch_mode: the selected type of batch
        :return: None
        """
        if self.batch.mode == batch_mode:
            self.batch.show()
        else:
            self.batch.quit()
            self.batch = Batch(self.master, self.dm, mode=batch_mode)

    def fill_devices(self):
        """ Fills the device selectors with the corresponding type of devices

        :return:
        """

        self.monochromator_box['values'] = self.dm.get_devices(
            ['Monochromator'])
        self.monochromator_box.current(0)
        self.acquisition_box['values'] = self.dm.get_devices(
            ['Lock-In', 'Spectrometer'])
        self.acquisition_box.current(0)

        self.select_monochromator()
        self.select_acquisition()

    def select_monochromator(self, *args):

        if self.monochromator is not None:
            self.dm.close_device(self.monochromator)

        dev_name = self.mono_var.get()
        self.monochromator = self.dm.open_device(dev_name)

        if self.monochromator is None:
            self.monochromator_box.current(0)
            self.monochromator = self.dm.open_device(self.mono_var.get())

        elif self.dm.current_config[dev_name]['Type'] == 'Monochromator':
            self.move = self.monochromator.move

        else:
            self.monochromator_box.current(0)
            self.monochromator = self.dm.open_device(self.mono_var.get())

        # If the device has an interface to set options, we link it to the entry in the menu
        interface = getattr(self.monochromator, "interface", None)
        if callable(interface):
            self.master.menu_hardware.entryconfig("Monochromator",
                                                  state="normal")
        else:
            self.master.menu_hardware.entryconfig("Monochromator",
                                                  state="disabled")

    def select_acquisition(self, *args):
        """ When the acquisition selector changes, this function updates some variables and the graphical interface
        to adapt it to the selected device.

        :param args: Dummy variable that does nothing but must exist (?)
        :return: None
        """

        if self.acquisition is not None:
            self.dm.close_device(self.acquisition)

        dev_name = self.acq_var.get()
        self.acquisition = self.dm.open_device(dev_name)

        if self.acquisition is None:
            self.acquisition_box.current(0)
            self.acquisition = self.dm.open_device(self.acq_var.get())

        elif self.dm.current_config[dev_name]['Type'] == 'Spectrometer':
            self.move = self.null
            self.prepare_scan = self.prepare_scan_spectrometer
            self.get_next_datapoint = self.mode_spectrometer
            self.start_live = self.prepare_live_spectrometer
            self.live = self.live_spectrometer
            self.background_frame.grid(column=0, row=4, sticky=(tk.NSEW))
            self.window_live_lbl.grid_forget()
            self.window_live_entry.grid_forget()
            self.monochromator_box['state'] = 'disabled'
            self.Step_entry['state'] = 'disabled'
            self.GoTo_button['state'] = 'disabled'
            self.GoTo_entry['state'] = 'disabled'

        elif self.dm.current_config[dev_name]['Type'] in [
                'Lock-In', 'Multimeter'
        ]:
            self.move = self.monochromator.move
            self.prepare_scan = self.prepare_scan_lockin
            self.get_next_datapoint = self.mode_lockin
            self.start_live = self.prepare_live_lockin
            self.live = self.live_lockin
            self.background_frame.grid_forget()
            self.window_live_lbl.grid(column=0, row=0, sticky=(tk.EW))
            self.window_live_entry.grid(column=1,
                                        row=0,
                                        columnspan=2,
                                        sticky=(tk.EW))
            self.monochromator_box['state'] = 'normal'
            self.Step_entry['state'] = 'normal'
            self.GoTo_button['state'] = 'normal'
            self.GoTo_entry['state'] = 'normal'

        else:
            self.acquisition_box.current(0)
            self.acquisition = self.dm.open_device(self.acq_var.get())

        interface = getattr(self.acquisition, "interface", None)
        if callable(interface):
            self.master.menu_hardware.entryconfig("acquisition",
                                                  state="normal")
        else:
            self.master.menu_hardware.entryconfig("acquisition",
                                                  state="disabled")

    def channel_param(self):
        """ When the channel parameters are changed, this function updates some internal variables and the graphical interface

        :return: None
        """
        ## Channel 1 labels ----------------------------------
        if self.Ch1_param1_var.get() == 'Xsignal' and self.Ch1_param2_var.get(
        ) == 'Rbaseline':
            self.plot_format['Ch1_ylabel'] = 'Xsignal/Rbaseline'
        elif self.Ch1_param1_var.get(
        ) == 'Xsignal' and self.Ch1_param2_var.get() == 'ND':
            self.plot_format['Ch1_ylabel'] = 'Xsignal'
        elif self.Ch1_param1_var.get(
        ) == 'Rsignal' and self.Ch1_param2_var.get() == 'Rbaseline':
            self.plot_format['Ch1_ylabel'] = 'Rsignal/Rbaseline'
        elif self.Ch1_param1_var.get(
        ) == 'Rsignal' and self.Ch1_param2_var.get() == 'ND':
            self.plot_format['Ch1_ylabel'] = 'Rsignal'
        ## Channel 2 labels ----------------------------------
        if self.Ch2_param_var.get() == 'Rbaseline':
            self.plot_format['Ch2_ylabel'] = 'Rbaseline'

        self.master.update_plot_axis(self.plot_format)

    def null(self, *args, **kwargs):
        """ Empty function that does nothing

        :return: None
        """
        pass

    def start_stop_scan(self):
        """ Starts and stops an scan

        :return: None
        """
        self.run_check()
        if self.stop:
            self.prepare_scan()
        else:
            self.stop = True
            self.finish_scan()

    def pause_scan(self):
        """ Pauses an scan or resumes the acquisition

        :return: None
        """
        self.stop = not self.stop

        if self.stop:
            self.pause_button['text'] = 'Resume'
        else:
            self.pause_button['text'] = 'Pause'

        self.get_next_datapoint()

    def prepare_scan_lockin(self):
        """ Any scan is divided in three stages:
        1) Prepare the conditions of the scan (this function), getting starting point, integration time and creating all relevant variables.
        2) Runing the scan, performed by a recursive function  "mode_spectrometer" or "mode_lockin"
        3) Finish the scan, where we update some variables and save the data.

        :return: None
        """

        self.update_integration_time()
        self.update_waiting_time()
        self.acquisition.open()
        # Get the scan conditions
        self.start_wl = max(float(self.Start_entry.get()), 250)
        self.stop_wl = max(min(float(self.Stop_entry.get()), 3000),
                           self.start_wl + 1)
        step = min(
            max(float(self.Step_entry.get()), self.acquisition.min_wavelength),
            self.stop_wl - self.start_wl)

        # # If we are in a batch, we proceed to the next point
        if self.batch.ready:
            self.batch.batch_proceed()

        self.move(self.start_wl, speed='Fast')
        print('Starting scan...')

        # Create the record array
        self.size = int(
            np.ceil((self.stop_wl - self.start_wl + 0.5 * step) / step))
        self.num = self.size

        self.record = np.zeros((self.size, 7))  ## Data to be saved
        self.record[:, 0] = np.arange(self.start_wl, self.stop_wl + 0.5 * step,
                                      step)
        self.record[:, 1] = self.record[:, 1] * np.NaN
        self.record[:, 2] = self.record[:, 1] * np.NaN
        self.record[:, 3] = self.record[:, 1] * np.NaN
        self.record[:, 4] = self.record[:, 1] * np.NaN

        self.plotdata = np.zeros((self.size, 3))  ## Data to be plotted
        self.plotdata[:, 0] = self.record[:, 0]
        self.plotdata[:, 1] = self.plotdata[:, 1] * np.NaN
        self.plotdata[:, 2] = self.plotdata[:, 1] * np.NaN

        self.master.prepare_meas(self.record)

        self.i = 0

        self.scan_running()

        self.mode_lockin()

    def mode_lockin(self):
        """ Gets the next data point in a scan. This function depends on the acquisition device

        :return: None
        """

        if not self.stop:
            Xc, Yc, Xsum, Ysum, Xdif, Ydif = self.acquisition.measure()

            ## Correct for RMS and half signals
            Xc = Xc * sqrt(2)
            Yc = Yc * sqrt(2)
            Xsum = Xsum * 2 * sqrt(2)
            Ysum = Ysum * 2 * sqrt(2)
            Xdif = Xdif * 2 * sqrt(2)
            Ydif = Ydif * 2 * sqrt(2)

            self.record[self.i, 1] = Xc
            self.record[self.i, 2] = Yc
            self.record[self.i, 3] = Xsum
            self.record[self.i, 4] = Ysum
            self.record[self.i, 5] = Xdif
            self.record[self.i, 6] = Ydif

            if self.plot_format['Ch1_ylabel'] == 'Xsignal/Rbaseline':
                self.plotdata[self.i, 1] = Xsum / sqrt(Xc**2 + Yc**2)
            elif self.plot_format['Ch1_ylabel'] == 'Rsignal/Rbaseline':
                self.plotdata[self.i, 1] = sqrt(Xsum**2 +
                                                Ysum**2) / sqrt(Xc**2 + Yc**2)
            elif self.plot_format['Ch1_ylabel'] == 'Xsignal':
                self.plotdata[self.i, 1] = Xsum
            elif self.plot_format['Ch1_ylabel'] == 'Rsignal':
                self.plotdata[self.i, 1] = sqrt(Xsum**2 + Ysum**2)
            else:
                self.plotdata[self.i, 1] = 0  ## Zero signal indicates an error
                print(
                    'The signal in Channel 1 is 0, possibly due to a communication Error'
                )

            if self.plot_format['Ch2_ylabel'] == 'Rbaseline':
                self.plotdata[self.i, 2] = sqrt(Xc**2 + Yc**2)
            else:
                self.plotdata[self.i, 2] = 0  ## Zero signal indicates an error
                print(
                    'The signal in Channel 2 is 0, possibly due to a communication Error'
                )

            self.master.update_plot(self.plotdata)

            if self.i < self.num - 1:
                self.i += 1
                self.move(self.record[self.i, 0], speed='Fast')
                self.master.window.after(
                    int(self.integration_time + self.waiting_time),
                    self.mode_lockin)

            else:
                self.finish_scan()

    def prepare_scan_spectrometer(self):
        """ Any scan is divided in three stages:
        1) Prepare the conditions of the scan (this function), getting starting point, integration time and creating all relevant variables.
        2) Runing the scan, performed by a recursive function "mode_spectrometer" or "mode_lockin"
        3) Finish the scan, where we update some variables and save the data.

        :return: None
        """
        self.options_mono = {
            'opt1': self.start_wl,
        }  #### Edit as required

        self.update_integration_time()
        self.update_waiting_time()

        # We check the background is not none and offer to update it
        if self.background is None:
            meas_bg = messagebox.askyesno(
                message=
                'There is no background spectrum for this integration time!',
                detail='Do you want to measure it now?',
                icon='question',
                title='Measure background?')

            self.get_background(meas_bg)

        # # If we are in a batch, we proceed to the next point
        if self.batch.ready:
            self.batch.batch_proceed()

        # If the integration time is too long, we have to split the acquisition in several steps,
        # otherwise the spectrometer hangs
        self.num = int(
            np.ceil(self.integration_time /
                    self.acquisition.max_integration_time))

        # Here we select the wavelength range we want to record and re-shape the record array
        # Get the scan conditions
        self.start_wl = max(float(self.Start_entry.get()), 300)
        self.stop_wl = max(min(float(self.Stop_entry.get()), 2000),
                           self.start_wl + 1)
        wl = self.acquisition.measure()[0]
        self.idx = np.where((self.start_wl <= wl) & (wl <= self.stop_wl))
        self.size = len(self.idx[0])

        # Create the record array
        self.record = np.zeros((self.size, 3))
        self.record[:, 0] = wl[self.idx]
        self.record[:, 2] = self.background[self.idx]

        self.master.prepare_meas(self.record)

        self.i = 0

        self.scan_running()

        self.mode_spectrometer()

    def mode_spectrometer(self):
        """ Gets the whole spectrum at once recorded by the spectrometer in the range selected

        :return: None
        """

        if not self.stop:
            data = self.acquisition.measure()
            intensity = data[1][self.idx] - self.background[self.idx]
            self.record[:, 1] = (intensity +
                                 self.i * self.record[:, 1]) / (self.i + 1.)

            self.master.update_plot(self.record)

            if self.i < self.num - 1:
                self.i = self.i + 1
                self.master.window.after(int(self.integration_time / self.num),
                                         self.mode_spectrometer)

            else:
                self.finish_scan()

    def finish_scan(self):
        """ Finish the scan, updating some global variables, saving the data in the temp file and offering to save the
        data somewhere else.

        :return: None
        """

        if self.batch.ready:
            self.master.finish_meas(self.record, finish=False)
            self.batch.batch_wrapup(self.record)
        else:
            self.master.finish_meas(self.record, finish=True)

        if self.stop or not self.batch.ready:
            # We reduce the number of counts in the batch to resume at the unfinished point if necessary
            # In other words, it repeats the last, un-finished measurement
            self.batch.count = max(self.batch.count - 1, 0)
            self.scan_stopped()

        else:
            self.prepare_scan()

    def check_inputs(self, parameter, value, min, max):
        """ Checks that the input parameters are within a specified range.

        :return: Error popup
        """
        if value < min or value > max:
            messagebox.showerror("Error", parameter + " is out of range!")
            self.run_button['state'] = 'enabled'
            self.run_ok.append(False)

        else:
            self.run_ok.append(True)

    def run_check(self):
        self.Start_wave = float(self.Start_entry.get())
        self.Stop_wave = float(self.Stop_entry.get())
        self.Step_wave = float(self.Step_entry.get())

        ## Check that inputs are within safe/correct limits
        self.run_ok = []
        self.check_inputs('Start wavelength', self.Start_wave, 100, 2000)
        self.check_inputs('End wavelength', self.Stop_wave, 100, 2000)
        self.check_inputs('Step', self.Step_wave, 0.1, 2000)

    def scan_running(self):
        """ Updates the graphical interface, disable the buttoms that must be disabled during the measurement.

        :return: None
        """
        self.scan_button['text'] = 'Stop'
        self.pause_button['state'] = 'enabled'
        self.record_live_button['state'] = 'disabled'
        self.GoTo_button['state'] = 'disabled'
        self.integration_time_button['state'] = 'disabled'
        self.waiting_time_button['state'] = 'disabled'
        self.background_button['state'] = 'disabled'
        self.clear_background_button['state'] = 'disabled'
        self.stop = False

    def scan_stopped(self):
        """ Returns the graphical interface to normal once the measurement has finished.

        :return: None
        """
        self.scan_button['text'] = 'Run'
        self.pause_button['state'] = 'disabled'
        self.record_live_button['state'] = 'enabled'
        self.GoTo_button['state'] = 'enabled'
        self.integration_time_button['state'] = 'enabled'
        self.waiting_time_button['state'] = 'enabled'
        self.background_button['state'] = 'enabled'
        self.clear_background_button['state'] = 'enabled'
        self.stop = True

    def get_background(self, meas_bg=True):
        """ Gets the background when using the spectrometer, if requested.

        :parameter: meas_bg     True or False: wether to measure a background or just produce a bg with zeros
        :return: None
        """

        if meas_bg:
            self.update_integration_time()
            self.background = self.acquisition.measure()[1]
            messagebox.showinfo(message='Background taken!',
                                detail='Press OK to continue.',
                                title='Background taken!')
        else:
            self.background = self.acquisition.measure()[1] * 0.0

    def clear_background(self):
        """ Clears the background when using the spectrometer.

        :return: None
        """
        self.background = None

    def record_live(self):
        """ Starts and stops a live recording.

        :return: None
        """
        if self.stop:
            self.stop = False
            self.record_live_button['text'] = 'Stop'
            self.pause_live_button['state'] = 'enabled'
            self.scan_button['state'] = 'disabled'
            self.background_button['state'] = 'disabled'
            self.clear_background_button['state'] = 'disabled'
            self.start_live()
        else:
            self.record_live_button['text'] = 'Record'
            self.pause_live_button['state'] = 'disabled'
            self.scan_button['state'] = 'enabled'
            self.background_button['state'] = 'enabled'
            self.clear_background_button['state'] = 'enabled'
            self.stop = True
            self.finish_live()

    def pause_live(self):
        """ Pauses a live recording or resumes the acquisition
        """
        self.stop = not self.stop

        if self.stop:
            self.pause_live_button['text'] = 'Resume'
        else:
            self.pause_live_button['text'] = 'Pause'

        self.live()

    def prepare_live_lockin(self):
        """ Prepares the lock-in live acquisition and prepare some variables
        """
        self.acquisition.open()
        self.goto()
        self.update_integration_time()

        self.window_points = int(self.window_live_entry.get())

        self.live_data = np.zeros((self.window_points, 3))
        self.live_data[:, 0] = np.arange(self.window_points)

        # Removes all plots, but not the data, and change the horizontal axis conditions
        self.master.clear_plot(xtitle='Time', ticks='off')
        self.master.prepare_meas(self.live_data)

        self.live_lockin()

    def live_lockin(self):
        """ Runs the live lock-in acquisition
        """
        if not self.stop:
            self.live_data[:-1, 1] = self.live_data[1:, 1]
            self.live_data[:-1, 2] = self.live_data[1:, 2]

            Xc, Yc, Xsum, Ysum, Xdif, Ydif = self.acquisition.measure()

            ## Correct for RMS and half signals
            Xc = Xc * sqrt(2)
            Yc = Yc * sqrt(2)
            Xsum = Xsum * 2 * sqrt(2)
            Ysum = Ysum * 2 * sqrt(2)
            Xdif = Xdif * 2 * sqrt(2)
            Ydif = Ydif * 2 * sqrt(2)

            if self.plot_format['Ch1_ylabel'] == 'Xsignal/Rbaseline':
                self.live_data[-1, 1] = Xsum / sqrt(Xc**2 + Yc**2)
            elif self.plot_format['Ch1_ylabel'] == 'Rsignal/Rbaseline':
                self.live_data[-1, 1] = sqrt(Xsum**2 + Ysum**2) / sqrt(Xc**2 +
                                                                       Yc**2)
            elif self.plot_format['Ch1_ylabel'] == 'Xsignal':
                self.live_data[-1, 1] = Xsum
            elif self.plot_format['Ch1_ylabel'] == 'Rsignal':
                self.live_data[-1, 1] = sqrt(Xsum**2 + Ysum**2)
            else:
                self.live_data[-1, 1] = 0  ## Zero signal indicates an error
                print(
                    'The signal in Channel 1 is 0, possibly due to a communication Error'
                )

            if self.plot_format['Ch2_ylabel'] == 'Rbaseline':
                self.live_data[-1, 2] = sqrt(Xc**2 + Yc**2)
            else:
                self.live_data[-1, 2] = 0  ## Zero signal indicates an error
                print(
                    'The signal in Channel 2 is 0, possibly due to a communication Error'
                )

            self.master.update_plot(self.live_data)

            self.master.window.after(self.integration_time, self.live_lockin)

    def prepare_live_spectrometer(self):
        """ Prepares the spectrometer live acquisition and prepare some variables
        """
        self.update_integration_time()

        if self.integration_time > self.acquisition.max_integration_time:
            self.integration_time = int(self.acquisition.max_integration_time)
            self.acquisition.update_integration_time(self.integration_time)

        data0, data1 = self.acquisition.measure()

        self.live_data = np.zeros((len(data0), 3))

        self.live_data[:, 0] = data0
        self.live_data[:, 1] = data1

        # Removes all plots, but not the data, and change the horizontal axis conditions
        self.master.clear_plot(xtitle='Wavelength (nm)', ticks='on')
        self.master.prepare_meas(self.live_data)

        self.live_spectrometer()

    def live_spectrometer(self):
        """ Runs the live spectrometer acquisition
        """
        if not self.stop:
            data0, data1 = self.acquisition.measure()
            self.live_data[:, 1] = data1

            self.master.update_plot(self.live_data)

            self.master.window.after(self.integration_time,
                                     self.live_spectrometer)

    def finish_live(self):
        """ Finish the live acquisition, returning the front end to the scan mode
        """
        self.master.replot_data(xtitle='Wavelength (nm)', ticks='on')

    def update_integration_time(self):
        """ Updates the integration time
        """
        old_integration_time = self.integration_time
        self.integration_time = int(self.integration_time_entry.get())

        if old_integration_time != self.integration_time:
            self.clear_background()
            self.integration_time = self.acquisition.update_integration_time(
                self.integration_time)
            self.integration_time_entry.delete(0, tk.END)
            self.integration_time_entry.insert(0, '%i' % self.integration_time)

    def update_waiting_time(self):
        """ Updates the waiting time
        """
        self.waiting_time = int(self.waiting_time_entry.get())

    def goto(self):
        """ Go to the specified wavelength
        """
        wl = float(self.GoTo_entry.get())
        self.move(wl, speed='Fast')
        print('Done! Wavelength = {} nm'.format(wl))