Esempio n. 1
0
class FocusPane(ttk.Frame):
    def __init__(self, graphpanes, datachannels, initial_drop_index=(0, 0)):
        self.graphpanes = graphpanes
        self.datachannels = datachannels

        self.fig = figure.Figure()
        super().__init__(master=graphpanes.mainframe, padding=0)
        self.canvas = FigureCanvasTkAgg(self.fig, master=self)
        self.plot_canvas = self.canvas.get_tk_widget()

        self.redraw = True
        self.firsttime = True

        self.ch_axes = []
        self.ch_lines = [""] * 2
        self.ch_text = []
        self.ch_min = []
        self.ch_max = []
        self.zero_lines = [""] * 2
        self.fig.subplots_adjust(left=0.05,
                                 top=0.95,
                                 bottom=0.05,
                                 right=0.95,
                                 hspace=0.15)

        self.plot_width = INITIAL_DATA_WIDTH
        self.curr_focus_ind = initial_drop_index

        ch_names = datachannels.get_ch_names()
        self.focus1 = tk.StringVar(self)
        self.focus1.set(ch_names[initial_drop_index[0]])

        self.focus2 = tk.StringVar(self)
        self.focus2.set(ch_names[initial_drop_index[1]])

        self.focus1_drop = ttk.OptionMenu(self, self.focus1,
                                          ch_names[initial_drop_index[0]],
                                          *tuple(ch_names))
        self.focus2_drop = ttk.OptionMenu(self, self.focus2,
                                          ch_names[initial_drop_index[1]],
                                          *tuple(ch_names))
        self.focus1_label = ttk.Label(self,
                                      text="Upper Figure Channel: ",
                                      justify=tk.RIGHT)
        self.focus2_label = ttk.Label(self,
                                      text="  Lower Figure Channel: ",
                                      justify=tk.RIGHT)
        self.focus1_label.grid(row=0,
                               column=0,
                               rowspan=1,
                               columnspan=1,
                               sticky='nsew')
        self.focus1_drop.grid(row=0,
                              column=1,
                              rowspan=1,
                              columnspan=1,
                              sticky='nsew')
        self.focus2_label.grid(row=0,
                               column=2,
                               rowspan=1,
                               columnspan=1,
                               sticky='nsew')
        self.focus2_drop.grid(row=0,
                              column=3,
                              rowspan=1,
                              columnspan=1,
                              sticky='nsew')

        self.plot_canvas.configure(background="black")
        self.plot_canvas.grid(row=1,
                              column=0,
                              rowspan=10,
                              columnspan=4,
                              sticky='nsew')
        self.rowconfigure(10, weight=1)
        for i in range(4):
            self.columnconfigure(i, weight=1)

        self.ani = animation.FuncAnimation(
            self.fig,
            self.animate,
            init_func=self.anim_init,
            interval=self.graphpanes.update_interval,
            blit=True)
        self.ani.event_source.stop()  # immediately stop animation

    def kill(self):
        if self.ani.event_source:
            self.ani.event_source.stop()

    def start(self):
        self.ani.event_source.start()

    def update_names(self, ch_names):
        menu1 = self.focus1_drop["menu"]
        menu1.delete(0, 'end')
        menu2 = self.focus2_drop["menu"]
        menu2.delete(0, 'end')
        self.focus1.set(ch_names[self.datachannels.ch_ids.index(
            ((self.focus1.get()).split()[0]).strip())])
        self.focus2.set(ch_names[self.datachannels.ch_ids.index(
            ((self.focus2.get()).split()[0]).strip())])
        for name in ch_names:
            menu1.add_command(label=name, command=tk._setit(self.focus1, name))
            menu2.add_command(label=name, command=tk._setit(self.focus2, name))
        self.redraw = True

    def anim_init(self):
        if self.firsttime:
            self.firsttime = False  # not sure why this is needed, but otherwise plot gets drawn twice!
            for i in range(2):
                sense_ch = self.datachannels[0]
                ch_data = sense_ch.data
                self.ch_axes.append(self.fig.add_subplot(2, 1, i + 1))
                self.ch_text.append(self.ch_axes[i].text(
                    self.plot_width * .1,
                    0,
                    "%.4g" % ch_data[-1] + " " + sense_ch.disp_unit,
                    fontsize=12,
                    ha="right",
                    va="bottom"))
                self.ch_text[i].set_color(sense_ch.color)

                self.ch_max.append(self.ch_axes[i].text(-self.plot_width,
                                                        1.02,
                                                        "1.0",
                                                        fontsize=8,
                                                        ha="left",
                                                        va="top"))
                self.ch_min.append(self.ch_axes[i].text(-self.plot_width,
                                                        -0.05,
                                                        "0.0",
                                                        fontsize=8,
                                                        ha="left",
                                                        va="bottom"))
                self.ch_axes[i].set_ylabel(sense_ch.disp_unit, fontsize=10)
                self.ch_axes[i].set_xlim(
                    (-self.plot_width, self.plot_width * .1))
                self.ch_axes[i].set_ylim((-0.05, 1.15))
                self.ch_axes[i].set_xticks([-self.plot_width, 0])
                self.ch_axes[i].tick_params(axis='x', labelsize=8)
                self.ch_axes[i].set_yticklabels([])
                self.zero_lines[i], = self.ch_axes[i].plot([0], [0],
                                                           '--',
                                                           color='white',
                                                           linewidth=0.6)
                self.ch_lines[i], = self.ch_axes[i].plot([0], [0],
                                                         sense_ch.color)

        return self.ch_lines + self.zero_lines + self.ch_text + self.ch_max + self.ch_min

    def animate(self, ind):
        self.graphpanes.mainwindow.event_generate(
            "<<Update>>", when='tail'
        )  # force main window to update the GUI process by clearing the data queue
        # Determine which sensor data we're plotting
        focus = [0, 0]
        focus[0] = self.datachannels.ch_ids.index(
            ((self.focus1.get()).split()[0]
             ).strip())  # get just the channel id part of the selection
        focus[1] = self.datachannels.ch_ids.index(
            ((self.focus2.get()).split()[0]).strip())

        # Determine if sensor selection plotting has changed
        if focus != self.curr_focus_ind:
            self.redraw = True
            self.curr_focus_ind = focus

        # determine number of elements on screen
        elements_on_screen = np.size(self.datachannels[0].data) - (np.abs(
            self.datachannels.time_data.data -
            self.datachannels.time_data.data[-1] + self.plot_width)).argmin()

        # Update plots
        for i in range(2):
            sense_ch = self.datachannels[
                focus[i]]  # the sensor channel being plotted
            ch_name = sense_ch.ch_id + ' -- ' + sense_ch.ch_desc
            # Mask NaN values in time and channel data
            nan_idx = np.isnan(sense_ch.data)
            ch_data = sense_ch.data[~nan_idx]
            time_data = self.datachannels.time_data.data[~nan_idx]
            data_to_consider = sense_ch.data[-(
                int)(self.graphpanes.consider_range * elements_on_screen):]
            data_to_consider = data_to_consider[~np.isnan(data_to_consider)]
            if np.size(ch_data) == 0:
                ch_data = np.zeros(1)
                time_data = np.zeros(1)

            if np.size(data_to_consider) == 0:
                data_to_consider = np.zeros(1)

            lims = (min(np.append(data_to_consider,
                                  [0])), max(data_to_consider) + 1E-10)
            if lims[0] < 0 and lims[1] < 0:  # if data is uniquely negative
                lims = (min(data_to_consider), 0)
            delta_lims = abs(lims[1] - lims[0])
            # remove old labels
            self.ch_max[i].remove()
            self.ch_min[i].remove()
            # reset data and max/min
            self.ch_text[i].set_text("%.4g" % ch_data[-1] + " " +
                                     sense_ch.disp_unit)
            self.ch_text[i].set_color(sense_ch.color)
            self.ch_text[i].set_position((self.plot_width * .1, 0))
            self.ch_lines[i].set_data(time_data - time_data[-1],
                                      (ch_data - lims[0]) / delta_lims)
            self.zero_lines[i].set_data(
                [-self.plot_width * 1.1, self.plot_width * 0.2],
                [-lims[0] / delta_lims, -lims[0] / delta_lims])
            self.ch_lines[i].set_color(sense_ch.color)
            self.ch_max[i] = self.ch_axes[i].text(-self.plot_width,
                                                  1.02,
                                                  '%.4g' % (lims[1]),
                                                  fontsize=8,
                                                  ha="left",
                                                  va="top")
            self.ch_min[i] = self.ch_axes[i].text(-self.plot_width,
                                                  -0.05,
                                                  '%.4g' % (lims[0]),
                                                  fontsize=8,
                                                  ha="left",
                                                  va="bottom")
            # if user has changed plot width, needs to be re-set-up and re-drawn
            if self.redraw:  #if user has adjusted the plot width or requested redraw
                self.ch_axes[i].set_xlim(
                    (-self.plot_width, self.plot_width * 0.1))
                self.ch_axes[i].set_xticks([-self.plot_width, 0])
                self.ch_axes[i].set_ylabel(sense_ch.disp_unit)
                self.ch_axes[i].set_title(ch_name)
                self.ch_axes[i].title.set_color(sense_ch.color)

        if self.redraw:  #if user has adjusted the plot width
            self.graphpanes.mainwindow.event_generate("<Configure>",
                                                      when='tail')
            # self.canvas.draw()
            self.canvas.resize_event()

        self.redraw = False
        return self.ch_lines + self.zero_lines + self.ch_text + self.ch_max + self.ch_min