Example #1
0
class PyplotEmbed(tk.Frame):
    """
    Class that will make a tkinter frame with a matplotlib plot area embedded in the frame
    """
    def __init__(self, master, toolbox_frame, plt_props, _master_frame, y_lims,
                 x_low, x_high):
        """
        Initialize the class with a parent of tkinter Frame and embed a pyplot graph in it
        The class also will have a list to hold the data series that is displayed
        :param toolbox_frame: tkinter frame that the toolbox can be shown in
        :param plt_props: properties of the pyplot
        :param _master: the frame that is the master to this frame
        :param _params: parameters needed for setting up the class
        :return:
        """
        tk.Frame.__init__(
            self, master=_master_frame)  # initialize with the parent class
        self.master = _master_frame
        self.l = None
        self.user_sets_labels_after_run = True
        self.label_instance = ""
        # Make an area to graph the data
        self.graph_area = tk.Frame(self)
        self.plotted_lines = [
        ]  # make a list to hold the Line2D to display in the graph
        self.data = _master_frame.data  # alias the data for this class to the main data

        self.legend_displayed = False

        # initiate the pyplot area
        self.init_graph_area(plt_props, toolbox_frame, y_lims, x_low, x_high)

        self.toolbar_status = False

    def init_graph_area(self, plt_props, toolbox_frame, y_lim, x_low, x_high):
        """
        take the tkinter Frame (self) and embed a pyplot figure into it
        :param plt_props: dictionary of properties of the pyplot
        :return: bind figure and axis to this instance
        """
        self.graph_area.figure_bed = plt.figure(figsize=(5, 4))
        self.graph_area.axis = plt.subplot(111)
        self.graph_area.axis.format_coord = lambda x, y: ""  # remove the coordinates in the toolbox
        # go through the plot properties and apply each one that is listed
        for key, value in plt_props.iteritems():
            eval("plt." + key + "(" + value + ")")
        # get the limits of the x axis from the parameters if they are not in the properties
        if "xlim" not in plt_props:
            plt.xlim(x_low, x_high)

        # calculate the current limit that can be reached, which depends on the resistor value
        #  of the TIAassume the adc can read +- 1V (1000 mV)
        plt.ylim(-y_lim, y_lim)
        # format the graph area, make the canvas and show it
        self.graph_area.figure_bed.set_facecolor('white')
        self.graph_area.canvas = FigureCanvasTkAgg(self.graph_area.figure_bed,
                                                   master=self)
        self.graph_area.canvas._tkcanvas.config(highlightthickness=0)
        # Make a binding for the user to change the data legend
        # uncomment below to start making a data legend editor
        self.graph_area.canvas.mpl_connect('button_press_event',
                                           self.legend_handler)
        # Make the toolbar and then unpack it.  allow the user to display or remove it later
        self.toolbar = NavToolbar(self.graph_area.canvas, toolbox_frame)
        self.toolbar.pack_forget()

        self.graph_area.canvas.draw()
        self.graph_area.canvas.get_tk_widget().pack(side='left',
                                                    fill=tk.BOTH,
                                                    expand=1)

    def update_data(self, x_data, y_data, _raw_y_data=None, label=None):
        print _raw_y_data
        if self.user_sets_labels_after_run:
            self.data.add_data(x_data, y_data, _raw_y_data)
            self.display_data()
            if not label:
                toplevel.UserSetDataLabel(self)
            else:
                self.change_label(label)
        else:
            self.data.add_data(x_data, y_data, _raw_y_data)
            self.display_data()

    def change_label(self, label, index=None):
        """
        :param label:
        :param index:
        :return:
        """
        if not index:
            index = self.data.index - 1

        self.data.change_label(label, index)
        self.update_legend()

    def add_notes(self, _notes):
        """ Save a note to the data to save with it
        :param _notes: string
        """
        self.data.notes[-1] = _notes

    def display_data_user_input(self, x_data, y_data):
        """ Update the label to what the user inputted, and display the data
        :param x_data: list - x-axis data
        :param y_data: list - y-axis data
        """
        label = self.label_instance
        self.display_data(x_data, y_data, self.data.index - 1)

    def display_data(self):
        """ Take in a x and y data set and plot them in the self instance of the pyplot
        :param x_data: x axis data
        :param y_data: y axis data
        :return:
        """
        index = self.data.index - 1  # it was incremented at the end of the add_data method
        x_data = self.data.voltage_data[index]
        y_data = self.data.current_data[index]
        _label = self.data.label[index]
        # if this is the first data series to be added the legend has to be displayed also
        if not self.legend_displayed:
            _box = self.graph_area.axis.get_position()
            self.graph_area.axis.set_position([
                _box.x0, _box.y0, _box.width * LEGEND_SPACE_RATIO, _box.height
            ])
            self.legend_displayed = True
        # add the data to the plot area and update the legend
        print len(x_data), len(y_data)
        print y_data
        l = self.graph_area.axis.plot(x_data, y_data, label=_label)
        self.data.colors.append(l[0].get_color())
        self.plotted_lines.append(l)
        self.update_legend()

    def update_amp_data(self, t, y, time_displayed):

        # self.graph_area.axis.clear()
        if not self.l and t:
            self.l, = self.graph_area.axis.plot(t, y)
            self.graph_area.axis.set_xlim(t[-1] - time_displayed, t[-1])
        elif y:
            self.l.set_ydata(y)
            self.l.set_xdata(t)
            self.graph_area.axis.set_xlim(t[-1] - time_displayed, t[-1])
            # self.graph_area.axis.set_ylim(0, 60000)
        self.graph_area.canvas.draw()

    def update_legend(self):
        """ Update the legend and redraw the graph
        """
        handle, labels = self.graph_area.axis.get_legend_handles_labels()

        self.graph_area.axis.legend(
            handle,
            self.data.label,
            loc='center left',
            bbox_to_anchor=(1, 0.5),
            title='Data series',
            prop={'size': 10},
            fancybox=True)  # not adding all this screws it up
        # up for some reason
        self.graph_area.canvas.show(
        )  # update the canvas where the data is being shown

    def delete_all_lines(self):
        """ Remove all the lines from the graph
        """
        logging.debug("deleting all lines")
        while self.plotted_lines:  # remove lines release all the memory
            l = self.plotted_lines.pop(0)
            l.pop().remove(
            )  # self.plotted_lines is a list of list so you have to pop twice
            del l  # see stackoverflow "how to remove lines in a matplotlib"

        # Update the legend with an empty data set but will keep the title and box showing
        # in the graph area
        self.update_legend()

    def delete_a_line(self, index):
        """ Delete a single line from the graph
        :param index:  int, index of which line to delete. the lines are saved in a list called
        self.plotted_lines
        """
        logging.debug("deleting line: %i", index)
        line = self.plotted_lines.pop(index)
        self.data.remove_data(index)
        line.pop().remove()
        del line  # release memory
        self.update_legend()

    def change_line_color(self, _color, index):
        """ Change the color of a line
        :param _color: tkinter color option to change to
        :param index: index of which line in the self.plotted_lines list to change
        """
        self.plotted_lines[index][0].set_color(_color)
        self.data.colors[index] = _color

    def update_graph(self):
        """ Redraw the graoh
        """
        self.graph_area.canvas.show()

    def toolbar_toggle(self):
        """ Display or remove the toolbar from the GUI
        """
        if self.toolbar_status:  # there is a toolbar, so remove it
            self.toolbar.pack_forget()
            self.toolbar_status = False
        else:  # no toolbar yet so add one
            self.toolbar.pack(side='left', anchor='w')
            self.toolbar_status = True

    def legend_handler(self, event):
        """ Bind event of clicking on the data legend to call up a top level to change the data
        style and label
        :param event: click event that occured
        """
        if event.x > (0.82 * self.winfo_width()
                      ):  # if mouse is clicked on the right side
            self.master.change_data_labels()

    def resize_x(self, x_low, x_high):
        """ Change the scale of the x axis
        :param x_low: lower limit on x axis
        :param x_high: upper limit on x axis
        """
        self.graph_area.axis.set_xlim([x_low, x_high])
        self.graph_area.canvas.show()

    def resize_y(self, _current_limit):
        """ Change the scale of the y axis
        :param _current_limit: most current (positive or negative)
        """
        self.graph_area.axis.set_ylim(-_current_limit * 1.2,
                                      _current_limit * 1.2)
        self.graph_area.canvas.show()
Example #2
0
class PyplotEmbed(tk.Frame):
    """
    Class that will make a tkinter frame with a matplotlib plot area embedded in the frame
    """
    def __init__(self, master, data):
        tk.Frame.__init__(self, master=master)
        self.index = 1
        self.lines = []
        self.vert_line = None
        self.data = data
        self.cursor_connect = None

        self.graph_area = tk.Frame(self)
        self.figure_bed = plt.figure(figsize=(6,4))
        self.axis = self.figure_bed.add_subplot(111)

        self.canvas = FigureCanvasTkAgg(self.figure_bed, master=self)
        self.canvas._tkcanvas.config(highlightthickness=0)
        self.toolbar = NavToolbar(self.canvas, self)  # TODO: check this
        # self.toolbar.pack_forget()
        self.toolbar.pack()

        self.canvas.draw()
        self.canvas.get_tk_widget().pack(side='left', fill=tk.BOTH, expand=1)

    def plot(self, data, _label):
        # self.data.plot(ax=self.graph_area.axis, label='channel {0}'.format(self.index))
        # line = self.axis.plot(data.index, data['voltage'], label=_label)[0]
        line = self.axis.plot(data.index, data, label=_label)[0]
        self.lines.append(line)
        self.axis.legend()
        self.index += 1
        self.canvas.show()

    def delete_all(self):
        while self.lines:
            l = self.lines.pop()
            l.remove()
            del l
        self.canvas.draw()
        self.index = 1
        self.axis.legend()

    def time_shift(self, data_index: int, time_to_shift: float):
        adj_time_shift = time_to_shift - self.data.time_start[data_index]

        x_data = self.data.adjusted_data[data_index].index - adj_time_shift
        self.lines[data_index].set_xdata(x_data)
        self.canvas.draw()

    def get_fit_start(self):
        print('make cursor')
        # cursor = self.axis.axvline(color='r')
        self.cursor_connect = self.canvas.mpl_connect('motion_notify_event', self.onMouseMove)
        self.canvas.mpl_connect('button_press_event', self.onclick)
        self.canvas.show()


    def onMouseMove(self, event):
        if not event.xdata:  # mouse is not over the plot area
            return
        if self.vert_line:
            self.vert_line.remove()
            del self.vert_line
            self.vert_line = None
        self.vert_line = self.axis.axvline(x=event.xdata, color='r', linewidth=2)
        self.canvas.show()

    def onclick(self, event):
        if event.dblclick == 1:
            full_data_set = self.data.adjusted_data[-1]
            start_index = full_data_set.index.get_loc(event.xdata, 'nearest')
            # start_index = pd.Index(self.data.adjusted_data[-1]).get_loc(event.xdata, 'nearest')
            start_num = full_data_set.index[start_index]
            data_to_fit = self.data.adjusted_data[-1][start_num:start_num+20]

            self.fit_data(data_to_fit, start_num)

    def fit_data(self, _data, starting_place):
        print('fitting')
        self.canvas.mpl_disconnect(self.cursor_connect)
        t = _data.index - starting_place # change the time so t=0 at the start of the recording
        amplitude_guess = -_data.voltage.min()
        time_shift = 0
        bounds = (
        0.0, [1.2*amplitude_guess, 1.2*amplitude_guess, 1.2*amplitude_guess, 1.2*amplitude_guess, 0.2, 2, 100, 100])
        initial_guess = (
        2. * amplitude_guess / 3, amplitude_guess / 3., amplitude_guess / 2., amplitude_guess / 2., 0.2, 2, 2, 15)

        fitted_parameters, _ = curve_fit(fitting_func_2a_2i_exp, t, _data.voltage,
                                         p0=initial_guess, bounds=bounds,
                                         ftol=0.0000005, xtol=0.0000005)
        # fitted_parameters, _ = curve_fit(fitting_func_2a_2i_exp, t, _data.voltage, p0=initial_guess)
        print('fitting params: {0}'.format(fitted_parameters))

        # make a new line with the fitted parameters and draw it
        y_fitted = fitting_func_2a_2i_exp(t, *fitted_parameters)
        # plt.plot(t + _data.index, y_fitted, linewidth=2, label="Check")
        fitted_line = self.axis.plot(_data.index,  y_fitted, linewidth=2, label='fitted')[0]
class PyplotEmbed(tk.Frame):
    """
    Class that will make a tkinter frame with a matplotlib plot area embedded in the frame
    """

    def __init__(self, root, toolbox_frame, plt_props, _master_frame, _params):
        """
        Initialize the class with a parent of tkinter Frame and embed a pyplot graph in it
        The class also will have a list to hold the data series that is displayed
        :param toolbox_frame: tkinter frame that the toolbox can be shown in
        :param plt_props: properties of the pyplot
        :param _master: the frame that is the master to this frame
        :param _params: parameters needed for setting up the class
        :return:
        """
        tk.Frame.__init__(self, master=_master_frame)  # initialize with the parent class
        self.master = _master_frame
        self.label_instance = ""
        """ Make an area to graph the data """
        self.graph_area = tk.Frame(self)
        self.params = root.operation_params
        self.plotted_lines = [] # make a list to hold the Line2D to display in the graph
        self.data = root.data  # alias the data for this class to the main data
        """ Create a list to hold the matplotlib lines """
        # self.plotted_lines = []
        # root.plotted_lines = self.plotted_lines

        self.params = _params
        self.legend_displayed = False

        """ initiate the pyplot area """

        self.init_graph_area(plt_props, toolbox_frame)

        self.toolbar_status = False

    def init_graph_area(self, plt_props, toolbox_frame):
        """
        take the tkinter Frame (self) and embed a pyplot figure into it
        :param plt_props: dictionary of properties of the pyplot
        :return: bind figure and axis to this instance
        """
        self.graph_area.figure_bed = plt.figure(figsize=(5, 4))
        self.graph_area.axis = plt.subplot(111)
        self.graph_area.axis.format_coord = lambda x, y: ""  # remove the coordinates in the toolbox
        """ go through the plot properties and apply each one that is listed """
        for key, value in plt_props.iteritems():
            eval("plt." + key + "(" + value + ")")
        """ get the limits of the x axis from the parameters if they are not in the properties """
        if "xlim" not in plt_props:
            plt.xlim(self.params['low_cv_voltage'], self.params['high_cv_voltage'])

        """ calculate the current limit that can be reached, which depends on the resistor value of the TIA
        assume the adc can read +- 1V (1000 mV)"""
        current_limit = 1000 / self.params['TIA_resistor']  # units: (mV/kohms) micro amperes
        plt.ylim(-current_limit, current_limit)
        """ format the graph area, make the canvas and show it """
        self.graph_area.figure_bed.set_facecolor('white')
        self.graph_area.canvas = FigureCanvasTkAgg(self.graph_area.figure_bed, master=self)
        self.graph_area.canvas._tkcanvas.config(highlightthickness=0)
        """ Make a binding for the user to change the data legend """
        # uncomment below to start making a data legend editor
        # self.graph_area.canvas.mpl_connect('button_press_event', self.legend_handler)
        """ Make the toolbar and then unpack it.  allow the user to display or remove it later """
        self.toolbar = NavToolbar(self.graph_area.canvas, toolbox_frame)
        self.toolbar.pack_forget()

        self.graph_area.canvas.draw()
        self.graph_area.canvas.get_tk_widget().pack(side='top', fill=tk.BOTH, expand=1)

    def update_data(self, x_data, y_data, _raw_y_data=None):

        if self.params['user_sets_labels_after_run']:
            self.data.add_data(x_data, y_data, _raw_y_data)
            self.display_data()
            toplevel.UserSetDataLabel(self)
        else:
            self.data.add_data(x_data, y_data, _raw_y_data)
            self.display_data()

    def change_label(self, label, index=None):

        if not index:
            index = self.data.index - 1

        self.data.change_label(label, index)
        self.update_legend()

    def add_notes(self, _notes):
        self.data.notes[-1] = _notes

    def display_data_user_input(self, x_data, y_data):
        label = self.label_instance

        self.display_data(x_data, y_data, self.data.index-1)

    def display_data(self):
        """
        Take in a x and y data set and plot them in the self instance of the pyplot
        :param x_data: x axis data
        :param y_data: y axis data
        :return:
        """
        index = self.data.index - 1  # it was incremented at the end of the add_data method
        x_data = self.data.x_data[index]
        y_data = self.data.y_data[index]
        _label = self.data.label[index]
        """ if this is the first data series to be added the legend has to be displayed also """
        if not self.legend_displayed:
            _box = self.graph_area.axis.get_position()
            self.graph_area.axis.set_position([_box.x0, _box.y0, _box.width * legend_space_ratio, _box.height])
            self.legend_displayed = True
        """ add the data to the plot area and update the legend """
        l = self.graph_area.axis.plot(x_data, y_data, label=_label)
        self.data.colors.append(l[0].get_color())
        self.plotted_lines.append(l)
        self.update_legend()

    def update_legend(self):
        handle, labels = self.graph_area.axis.get_legend_handles_labels()

        self.graph_area.axis.legend(handle, self.data.label,
                                    loc='center left',
                                    bbox_to_anchor=(1, 0.5),
                                    title='Data series',
                                    prop={'size': 10},
                                    fancybox=True)  # not adding all this screws it up for some reason
        # self.graph_area.axis.legend.get_frame().set_alpha(0.5)
        self.graph_area.canvas.show()  # update the canvas where the data is being shown

    def delete_all_lines(self):
        logging.debug("deleting all lines")
        while self.plotted_lines:
            l = self.plotted_lines.pop(0)
            l.pop().remove()  # self.plotted_lines is a list of list so you have to pop twice
            del l  # see stackoverflow "how to remove lines in a matplotlib", this is needed to release the memory

        """ Update the legend with an empty data set but will keep the title and box showing in the graph area """
        self.update_legend()

    def delete_a_line(self, index):
        logging.debug("deleting line: %i", index)
        line = self.plotted_lines.pop(index)
        self.data.remove_data(index)
        line.pop().remove()
        del line

        self.update_legend()

    def change_line_color(self, _color, index):
        logging.debug("change line color: ", _color, index)
        print self.plotted_lines
        print self.plotted_lines[index]
        self.plotted_lines[index][0].set_color(_color)
        self.data.colors[index] = _color

    def update_graph(self):
        self.graph_area.canvas.show()

    def toolbar_toggle(self):
        """
        Display or remove the toolbar from the GUI
        :return:
        """
        if self.toolbar_status:  # there is a toolbar, so remove it
            self.toolbar.pack_forget()
            self.toolbar_status = False
        else:  # no toolbar yet so add one
            self.toolbar.pack(side='left', anchor='w')
            self.toolbar_status = True

    def legend_handler(self, event):
        """
        :param event:
        :return:
        """
        if event.x > (0.8 * self.winfo_width()):  # if mouse is clicked on the right side
            print "on right side"
            legend_top.DataLegendTop(self)

    def resize_x(self, x_low, x_high):
        """
        Change the scale of the x axis
        :param x_low: lower limit on x axis
        :param x_high: upper limit on x axis
        :return:
        """
        self.graph_area.axis.set_xlim([x_low, x_high])
        self.graph_area.canvas.show()

    def resize_y(self, _current_limit):
        """
        Change the scale of the y axis
        :param _current_limit: most current (positive or negative)
        :return:
        """
        self.graph_area.axis.set_ylim(-_current_limit, _current_limit)
        self.graph_area.canvas.show()
Example #4
0
class PyplotEmbed(tk.Frame):
    """
    Class that will make a tkinter frame with a matplotlib plot area embedded in the frame
    """
    def __init__(self, root, toolbox_frame, plt_props, _master_frame, _params):
        """
        Initialize the class with a parent of tkinter Frame and embed a pyplot graph in it
        The class also will have a list to hold the data series that is displayed
        :param toolbox_frame: tkinter frame that the toolbox can be shown in
        :param plt_props: properties of the pyplot
        :param _master: the frame that is the master to this frame
        :param _params: parameters needed for setting up the class
        :return:
        """
        tk.Frame.__init__(
            self, master=_master_frame)  # initialize with the parent class
        self.master = _master_frame
        self.label_instance = ""
        """ Make an area to graph the data """
        self.graph_area = tk.Frame(self)
        self.params = root.operation_params
        self.plotted_lines = [
        ]  # make a list to hold the Line2D to display in the graph
        self.data = root.data  # alias the data for this class to the main data
        """ Create a list to hold the matplotlib lines """
        # self.plotted_lines = []
        # root.plotted_lines = self.plotted_lines

        self.params = _params
        self.legend_displayed = False
        """ initiate the pyplot area """

        self.init_graph_area(plt_props, toolbox_frame)

        self.toolbar_status = False

    def init_graph_area(self, plt_props, toolbox_frame):
        """
        take the tkinter Frame (self) and embed a pyplot figure into it
        :param plt_props: dictionary of properties of the pyplot
        :return: bind figure and axis to this instance
        """
        self.graph_area.figure_bed = plt.figure(figsize=(5, 4))
        self.graph_area.axis = plt.subplot(111)
        self.graph_area.axis.format_coord = lambda x, y: ""  # remove the coordinates in the toolbox
        """ go through the plot properties and apply each one that is listed """
        for key, value in plt_props.iteritems():
            eval("plt." + key + "(" + value + ")")
        """ get the limits of the x axis from the parameters if they are not in the properties """
        if "xlim" not in plt_props:
            plt.xlim(self.params['low_cv_voltage'],
                     self.params['high_cv_voltage'])
        """ calculate the current limit that can be reached, which depends on the resistor value of the TIA
        assume the adc can read +- 1V (1000 mV)"""
        current_limit = 1000 / self.params[
            'TIA_resistor']  # units: (mV/kohms) micro amperes
        plt.ylim(-current_limit, current_limit)
        """ format the graph area, make the canvas and show it """
        self.graph_area.figure_bed.set_facecolor('white')
        self.graph_area.canvas = FigureCanvasTkAgg(self.graph_area.figure_bed,
                                                   master=self)
        self.graph_area.canvas._tkcanvas.config(highlightthickness=0)
        """ Make a binding for the user to change the data legend """
        # uncomment below to start making a data legend editor
        # self.graph_area.canvas.mpl_connect('button_press_event', self.legend_handler)
        """ Make the toolbar and then unpack it.  allow the user to display or remove it later """
        self.toolbar = NavToolbar(self.graph_area.canvas, toolbox_frame)
        self.toolbar.pack_forget()

        self.graph_area.canvas.draw()
        self.graph_area.canvas.get_tk_widget().pack(side='top',
                                                    fill=tk.BOTH,
                                                    expand=1)

    def update_data(self, x_data, y_data, _raw_y_data=None):

        if self.params['user_sets_labels_after_run']:
            self.data.add_data(x_data, y_data, _raw_y_data)
            self.display_data()
            toplevel.UserSetDataLabel(self)
        else:
            self.data.add_data(x_data, y_data, _raw_y_data)
            self.display_data()

    def change_label(self, label, index=None):

        if not index:
            index = self.data.index - 1

        self.data.change_label(label, index)
        self.update_legend()

    def add_notes(self, _notes):
        self.data.notes[-1] = _notes

    def display_data_user_input(self, x_data, y_data):
        label = self.label_instance

        self.display_data(x_data, y_data, self.data.index - 1)

    def display_data(self):
        """
        Take in a x and y data set and plot them in the self instance of the pyplot
        :param x_data: x axis data
        :param y_data: y axis data
        :return:
        """
        index = self.data.index - 1  # it was incremented at the end of the add_data method
        x_data = self.data.x_data[index]
        y_data = self.data.y_data[index]
        _label = self.data.label[index]
        """ if this is the first data series to be added the legend has to be displayed also """
        if not self.legend_displayed:
            _box = self.graph_area.axis.get_position()
            self.graph_area.axis.set_position([
                _box.x0, _box.y0, _box.width * legend_space_ratio, _box.height
            ])
            self.legend_displayed = True
        """ add the data to the plot area and update the legend """
        l = self.graph_area.axis.plot(x_data, y_data, label=_label)
        self.data.colors.append(l[0].get_color())
        self.plotted_lines.append(l)
        self.update_legend()

    def update_legend(self):
        handle, labels = self.graph_area.axis.get_legend_handles_labels()

        self.graph_area.axis.legend(
            handle,
            self.data.label,
            loc='center left',
            bbox_to_anchor=(1, 0.5),
            title='Data series',
            prop={'size': 10},
            fancybox=True)  # not adding all this screws it up for some reason
        # self.graph_area.axis.legend.get_frame().set_alpha(0.5)
        self.graph_area.canvas.show(
        )  # update the canvas where the data is being shown

    def delete_all_lines(self):
        logging.debug("deleting all lines")
        while self.plotted_lines:
            l = self.plotted_lines.pop(0)
            l.pop().remove(
            )  # self.plotted_lines is a list of list so you have to pop twice
            del l  # see stackoverflow "how to remove lines in a matplotlib", this is needed to release the memory
        """ Update the legend with an empty data set but will keep the title and box showing in the graph area """
        self.update_legend()

    def delete_a_line(self, index):
        logging.debug("deleting line: %i", index)
        line = self.plotted_lines.pop(index)
        self.data.remove_data(index)
        line.pop().remove()
        del line

        self.update_legend()

    def change_line_color(self, _color, index):
        logging.debug("change line color: ", _color, index)
        print self.plotted_lines
        print self.plotted_lines[index]
        self.plotted_lines[index][0].set_color(_color)
        self.data.colors[index] = _color

    def update_graph(self):
        self.graph_area.canvas.show()

    def toolbar_toggle(self):
        """
        Display or remove the toolbar from the GUI
        :return:
        """
        if self.toolbar_status:  # there is a toolbar, so remove it
            self.toolbar.pack_forget()
            self.toolbar_status = False
        else:  # no toolbar yet so add one
            self.toolbar.pack(side='left', anchor='w')
            self.toolbar_status = True

    def legend_handler(self, event):
        """
        :param event:
        :return:
        """
        if event.x > (0.8 * self.winfo_width()
                      ):  # if mouse is clicked on the right side
            print "on right side"
            legend_top.DataLegendTop(self)

    def resize_x(self, x_low, x_high):
        """
        Change the scale of the x axis
        :param x_low: lower limit on x axis
        :param x_high: upper limit on x axis
        :return:
        """
        self.graph_area.axis.set_xlim([x_low, x_high])
        self.graph_area.canvas.show()

    def resize_y(self, _current_limit):
        """
        Change the scale of the y axis
        :param _current_limit: most current (positive or negative)
        :return:
        """
        self.graph_area.axis.set_ylim(-_current_limit, _current_limit)
        self.graph_area.canvas.show()
class PyplotEmbed(tk.Frame):
    """ Class that will make a tkinter frame with a matplotlib plot area embedded in the frame
    """
    def __init__(self, master, data):
        tk.Frame.__init__(self, master=master)
        self.index = 1
        self.lines = []
        self.labels = []
        self.colors = []
        self.vert_line = None
        self.data = data
        self.cursor_connect = None

        self.graph_area = tk.Frame(self)
        self.figure_bed = plt.figure(figsize=(6, 4))
        self.axis = self.figure_bed.add_subplot(111)

        self.canvas = FigureCanvasTkAgg(self.figure_bed, master=self)
        self.canvas._tkcanvas.config(highlightthickness=0)
        self.toolbar = NavToolbar(self.canvas, self)  # TODO: check this
        # self.toolbar.pack_forget()
        self.toolbar.pack()

        self.canvas.draw()
        self.canvas.get_tk_widget().pack(side='left', fill=tk.BOTH, expand=1)

    def plot(self, data, _label, color=None):
        # self.data.plot(ax=self.graph_area.axis, label='channel {0}'.format(self.index))
        # line = self.axis.plot(data.index, data['voltage'], label=_label)[0]
        line = self.axis.plot(data.index, data, label=_label)[0]
        if color:
            line.set_color(color)
        self.colors.append(line.get_color())
        self.labels.append(_label)
        self.lines.append(line)
        self.axis.legend()
        self.index += 1
        self.canvas.show()

    def delete_all(self):
        while self.lines:
            l = self.lines.pop()
            l.remove()
            del l
        self.canvas.draw()
        self.index = 1
        self.axis.legend()

    def delete_some_data(self, picks):
        print('delete in graph: ', picks)
        for index in reversed(picks):
            self.delete_line(index)

    def delete_line(self, _index):
        self.index -= 1
        del self.labels[_index]
        line = self.lines.pop(_index)
        line.remove()
        del line
        self.update_legend()

    def change_line_color(self, _color, index):
        print('change line:', index, _color)
        self.lines[index].set_color(_color)
        self.colors[index] = _color

    def change_line_style(self, style, index):
        if style != 'solid':
            self.lines[index].set_linestyle(style)
            self.lines[index].set_dashes((1, 5))

    def update_legend(self):
        """ Update the legend and redraw the graph """
        handle, labels = self.axis.get_legend_handles_labels()

        self.axis.legend(
            handle,
            self.labels,
            loc='best',
            # bbox_to_anchor=(1, 0.5),
            # title='Data series',
            prop={'size': 10},
            fancybox=True)  # not adding all this screws it up
        # up for some reason
        self.canvas.show()  # update the canvas where the data is being shown

    def time_shift(self, data_index: int, time_to_shift: float):
        adj_time_shift = time_to_shift - self.data.time_start[data_index]

        x_data = self.data.adjusted_data[data_index].index - adj_time_shift
        self.lines[data_index].set_xdata(x_data)
        self.canvas.draw()

    def get_fit_start(self):
        print('make cursor')
        # cursor = self.axis.axvline(color='r')
        self.cursor_connect = self.canvas.mpl_connect('motion_notify_event',
                                                      self.onMouseMove)
        self.canvas.mpl_connect('button_press_event', self.onclick)
        self.canvas.show()

    def onMouseMove(self, event):
        if not event.xdata:  # mouse is not over the plot area
            return
        if self.vert_line:
            self.vert_line.remove()
            del self.vert_line
            self.vert_line = None
        self.vert_line = self.axis.axvline(x=event.xdata,
                                           color='r',
                                           linewidth=2)
        self.canvas.show()

    def onclick(self, event):
        if event.dblclick == 1:
            full_data_set = self.data.adjusted_data[-1]
            start_index = full_data_set.index.get_loc(event.xdata, 'nearest')
            # start_index = pd.Index(self.data.adjusted_data[-1]).get_loc(event.xdata, 'nearest')
            start_num = full_data_set.index[start_index]
            data_to_fit = self.data.adjusted_data[-1][start_num:start_num + 20]

            self.fit_data(data_to_fit, start_num)

    def fit_data(self, _data, starting_place):
        print('fitting')
        self.canvas.mpl_disconnect(self.cursor_connect)
        t = _data.index - starting_place  # change the time so t=0 at the start of the recording
        amplitude_guess = -_data.voltage.min()
        time_shift = 0
        bounds = (0.0, [
            1.2 * amplitude_guess, 1.2 * amplitude_guess,
            1.2 * amplitude_guess, 1.2 * amplitude_guess, 0.2, 2, 100, 100
        ])
        initial_guess = (2. * amplitude_guess / 3, amplitude_guess / 3.,
                         amplitude_guess / 2., amplitude_guess / 2., 0.2, 2, 2,
                         15)

        fitted_parameters, _ = curve_fit(fitting_func_2a_2i_exp,
                                         t,
                                         _data.voltage,
                                         p0=initial_guess,
                                         bounds=bounds,
                                         ftol=0.0000005,
                                         xtol=0.0000005)
        # fitted_parameters, _ = curve_fit(fitting_func_2a_2i_exp, t, _data.voltage, p0=initial_guess)
        print('fitting params: {0}'.format(fitted_parameters))

        # make a new line with the fitted parameters and draw it
        y_fitted = fitting_func_2a_2i_exp(t, *fitted_parameters)
        # plt.plot(t + _data.index, y_fitted, linewidth=2, label="Check")
        fitted_line = self.axis.plot(_data.index,
                                     y_fitted,
                                     linewidth=2,
                                     label='fitted')[0]

    def toogle_data_decimation(self, data):
        # line: matplotlib.lines.Line2D  # type hint the for loop variable
        for i, line in enumerate(self.lines):
            line.set_xdata(data[i].index.values)
            line.set_ydata(data[i])
        self.canvas.show()