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