class MPLWidget(qw.QWidget): labels_changed = QtCore.pyqtSignal(np.ndarray, np.ndarray) def __init__(self, parent, timestamps=None, n_states=10, state_min=0, width=5, height=8, dpi=100, static_data=None, overlay_data=None): super(MPLWidget, self).__init__() self.parent = parent self.timestamps = timestamps self.n_states = n_states self.state_min = state_min self.static_data = static_data self.overlay_data = overlay_data self.fig = Figure((width, height), dpi=dpi) self.ax = self.fig.add_subplot(111) self.canvas = FigureCanvasQTAgg(self.fig) self.canvas.setParent(self) self.canvas.setFocusPolicy(QtCore.Qt.StrongFocus) self.canvas.setFocus() self.toolbar = NavigationToolbar(self.canvas, self) self.zoom_active = False for child in self.toolbar.children(): if isinstance(child, qw.QToolButton) and child.text() == 'Zoom': child.toggled.connect(self.zoom_button_toggled) self.init_figure() self.canvas.mpl_connect('button_press_event', self.button_pressed) self.canvas.mpl_connect('button_release_event', self.button_released) self.canvas.mpl_connect('motion_notify_event', self.mouse_moved) vbox = qw.QVBoxLayout() vbox.addWidget(self.toolbar) vbox.addWidget(self.canvas) self.setLayout(vbox) def init_figure(self): ax = self.ax x = self.timestamps y = np.zeros_like(x) self.lines = {} for i in range(self.n_states): self.lines[i] = ax.plot(x, y, 'ko-')[0] self.pos_line = ax.plot([0, 0], [0, self.n_states], '-', color=3 * [.5])[0] if self.overlay_data is not None and \ self.overlay_data.data is not None: ax.plot(self.overlay_data.timestamps, self.overlay_data.data, '-', color=3 * [.74], lw=.5) y_min = -1 if self.static_data is not None: if not isinstance(self.static_data, list): static_data = [self.static_data] else: static_data = self.static_data colors = ['r', 'b', 'o'] for i, dd in enumerate(static_data): x = dd.timestamps y = dd.data ax.plot(x, -2 - i * 2 + y / (4 * y.std()), '-', color=colors[i], alpha=.25) y_min -= 1.5 ax.set_xlim(x.min() - 1, x.max() + 1) ax.set_xlabel('Time (s)') ax.set_ylabel('Assigned state') ax.set_ylim(y_min, self.n_states) ax.set_yticks(np.arange(self.state_min, self.n_states)) ax.xaxis.set_major_locator(plt.MaxNLocator(5)) # Hide the right and top spines ax.spines['right'].set_visible(False) ax.spines['top'].set_visible(False) # Only show ticks on the left and bottom spines ax.yaxis.set_ticks_position('left') ax.xaxis.set_ticks_position('bottom') ax.tick_params(axis='both', which='major', labelsize=8) ax.xaxis.label.set_fontsize(10) ax.xaxis.label.set_fontname('Arial') ax.yaxis.label.set_fontsize(10) ax.xaxis.label.set_fontname('Arial') for at in ax.texts: at.set_fontsize(10) at.set_fontname('Arial') self.fig.canvas.draw() def plot_labels(self, y, pos=-1): x = self.timestamps for i in range(self.n_states): yi = np.ma.masked_where(y != i, y) self.lines[i].set_data(x, yi) self.ax.draw_artist(self.lines[i]) if pos >= 0: self.pos_line.set_data(2 * [x[pos]], [0, self.n_states]) self.ax.draw_artist(self.pos_line) self.fig.canvas.draw() def update_plot(self): for i in range(self.n_states): self.ax.draw_artist(self.lines[i]) self.ax.draw_artist(self.pos_line) self.fig.canvas.draw() def update_figure(self): self.fig.canvas.draw() def button_pressed(self, event): self.x_start = event.xdata self.y_start = event.ydata def zoom_button_toggled(self, event): self.zoom_active = event def button_released(self, event): if not self.zoom_active: x1 = self.x_start y1 = self.y_start x2 = event.xdata y2 = event.ydata if x1 is not None and x2 is not None and x1 != x2: if x1 > x2: x1, x2 = x2, x1 y1, y2 = y2, y1 slope = (y2 - y1) / (x2 - x1) intercept = y1 - slope * x1 ts = self.timestamps ind = np.where(np.logical_and(ts >= x1, ts <= x2))[0] yy = ts[ind] * slope + intercept labels = np.asarray(np.round(yy), np.int) self.labels_changed.emit(ind, labels) def mouse_moved(self, event): pass
class MatplotlibWidget(QtGui.QWidget): '''Base class for matplotlib widgets ''' def __init__(self, parent): '''Inits matplotlib widget. Args: parent: A Parent class object. ''' super().__init__() self.main_frame = parent self.dpi = 75 self.show_axis_ticks = True self.__create_frame() def __create_frame(self): self.fig = Figure((5.0, 3.0), dpi=self.dpi) self.fig.patch.set_facecolor("white") self.canvas = FigureCanvas(self.fig) self.canvas.manager = MockManager(self.main_frame) self.canvas.setParent(self.main_frame) self.axes = self.fig.add_subplot(111) self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) hbox = QtGui.QHBoxLayout() self.main_frame.ui.matplotlib_layout.addWidget(self.canvas) self.main_frame.ui.matplotlib_layout.addWidget(self.mpl_toolbar) self.main_frame.ui.matplotlib_layout.addLayout(hbox) def fork_toolbar_buttons(self): '''Remove figure options & subplot config that might not work properly. ''' try: self.mpl_toolbar.removeAction(self.mpl_toolbar.children()[21]) self.mpl_toolbar.removeAction(self.mpl_toolbar.children()[17]) except: pass # Already removed def remove_axes_ticks(self): '''Remove ticks from axes. ''' if not self.show_axis_ticks: for tick in self.axes.yaxis.get_major_ticks(): tick.label1On = False tick.label2On = False for tick in self.axes.xaxis.get_major_ticks(): tick.label1On = False tick.label2On = False def delete(self): '''Delete matplotlib objects. ''' self.axes.clear() # Might be useless with fig.clf() self.canvas.close() self.fig.clf() self.close() del self.fig del self.canvas del self.axes import gc gc.collect()