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
Ejemplo n.º 2
0
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()