Пример #1
0
class Spectrogram(StandardMonitorPage):
    """Main class for a page that generates real-time spectrogram plots of EEG.
    """
    def __init__(self, *args, **kwargs):
        """Construct a new Spectrogram page.

        Args:
            *args, **kwargs:  Arguments to pass to the Page base class.
        """
        self.initConfig()

        # initialize Page base class
        StandardMonitorPage.__init__(self,
                                     name='Spectrogram',
                                     configPanelClass=ConfigPanel,
                                     *args,
                                     **kwargs)

        self.initCanvas()
        self.initLayout()

    def initConfig(self):
        self.filter = True  # use raw or filtered signal
        self.chanIndex = 0  # index of channel to show
        self.width = 5.0  # width of window to use for computing PSD

        self.decimationFactor = 1  # decimation factor, e.g., 2 will decimate to half sampRate

        self.interpolation = 'none'

        self.normScale = 'log'
        self.scale = -2

        self.method = 'Wavelet'

        self.setRefreshDelay(200)

        self.waveletConfig = util.Holder(nFreq=100, span=10)

        self.fourierConfig = util.Holder()

    def initCanvas(self):
        """Initialize a new matplotlib canvas, figure and axis.
        """
        self.plotPanel = wx.Panel(self)
        self.plotPanel.SetBackgroundColour('white')
        plotSizer = wx.BoxSizer(orient=wx.VERTICAL)
        self.plotPanel.SetSizer(plotSizer)

        self.fig = plt.Figure(facecolor='white')
        #self.canvas = FigureCanvas(parent=self, id=wx.ID_ANY, figure=self.fig)
        self.canvas = FigureCanvas(parent=self.plotPanel,
                                   id=wx.ID_ANY,
                                   figure=self.fig)

        self.ax = self.fig.add_subplot(1, 1, 1)
        self.ax.set_xlabel('Time (s)')
        self.ax.set_ylabel('Frequency (Hz)')

        self.cbAx = self.fig.add_axes([0.91, 0.05, 0.03, 0.93])

        #self.fig.subplots_adjust(hspace=0.0, wspace=0.0,
        #    left=0.035, right=0.92, top=0.98, bottom=0.05)

        self.adjustMargins()

        self.firstPlot()

        self.lastSize = (0, 0)
        self.needsResizePlot = True
        self.canvas.Bind(wx.EVT_SIZE, self.resizePlot)
        self.canvas.Bind(wx.EVT_IDLE, self.idleResizePlot)

        ##self.plotToolbar = widgets.PyPlotNavbar(self.canvas)
        ##plotSizer.Add(self.plotToolbar, proportion=0, flag=wx.EXPAND)
        plotSizer.Add(self.canvas, proportion=1, flag=wx.EXPAND)

        #self.plotToolbar.Hide()

    def initLayout(self):
        self.initStandardLayout()

        plotPaneAuiInfo = aui.AuiPaneInfo().Name('canvas').Caption(
            'Spectrogram').CenterPane()
        #self.auiManager.AddPane(self.canvas, plotPaneAuiInfo)
        self.auiManager.AddPane(self.plotPanel, plotPaneAuiInfo)

        self.auiManager.Update()

        self.canvas.Hide()

    def afterUpdateSource(self):
        self.configPanel.updateChannels()

    def afterStart(self):
        # make sure canvas is visible
        self.canvas.Show()
        self.plotPanel.Layout()

        # trigger initial plot update
        self.needsFirstPlot = True

    def getCap(self):
        cap = self.src.getEEGSecs(self.width, filter=self.filter, copy=False)
        if self.decimationFactor > 1:
            cap.decimate(self.decimationFactor)

        return cap

    def getSpectrum(self, cap):
        # configurable XXX - idfah
        data = cap.data[:, self.chanIndex] * sig.windows.tukey(
            cap.data.shape[0])  # tukey or hann? XXX - idfah

        freqs, powers, phases = self.cwt.apply(data)

        # configurable XXX - idfah
        powers = np.clip(powers, 1.0e-10, np.inf)

        return freqs, powers

    def firstPlot(self, event=None):
        cap = self.getCap()

        self.cwt = sig.CWT(sampRate=cap.getSampRate(),
                           freqs=self.waveletConfig.nFreq,
                           span=self.waveletConfig.span)

        if self.isRunning():
            freqs, powers = self.getSpectrum(cap)
        else:
            freqs = np.arange(1, self.src.getSampRate() // 2 + 1)
            powers = np.zeros((128, 10, 1))
            powers[0, 0, 0] = 1.0

        self.ax.cla()
        self.cbAx.cla()

        self.ax.set_xlabel('Time (s)')
        self.ax.set_ylabel('Frequency (Hz)')

        self.wimg = self.ax.imshow(powers[:, :, 0].T,
                                   interpolation=self.interpolation,
                                   origin='lower',
                                   aspect='auto',
                                   norm=self.getNorm(),
                                   extent=self.getExtent(cap, freqs),
                                   cmap=plt.cm.get_cmap('jet'),
                                   animated=True)

        self.cbar = self.fig.colorbar(self.wimg, cax=self.cbAx)
        self.cbar.set_label(r'Power Density ($V^2 / Hz$)')

        #self.updateNorm(powers)

        self.canvas.draw()

        #self.background = self.canvas.copy_from_bbox(self.fig.bbox)
        self.background = self.canvas.copy_from_bbox(self.ax.bbox)

        self.needsFirstPlot = False

    def adjustMargins(self):
        self.fig.subplots_adjust(hspace=0.0,
                                 wspace=0.0,
                                 left=0.045,
                                 right=0.90,
                                 top=0.98,
                                 bottom=0.07)

    def resizePlot(self, event):
        # prevents handling extra resize events, hack XXX - idfah
        size = self.canvas.GetSize()
        if self.lastSize == size:
            return
        else:
            self.lastSize = size

        # this is all a hack to do resizing on idle when page is not running
        # should this be a custom FigureCanvas derived widget? XXX - idfah
        if self.isRunning():
            # when running, just do event.Skip() this will
            # call canvas._onSize since it is second handler
            self.needsResizePlot = False
            event.Skip()
        else:
            # flag to resize on next idle event
            self.needsResizePlot = True

    def idleResizePlot(self, event):
        # if not running and flagged for resize
        if not self.isRunning() and self.needsResizePlot:
            ##self.adjustMargins()
            self.needsResizePlot = False
            # call canvas resize method manually
            # hack alert, we just pass None as event
            # since it's not used anyway
            self.canvas._onSize(None)

    def getExtent(self, cap, freqs):
        return (0.0, cap.getNObs() / float(cap.getSampRate()), np.min(freqs),
                np.max(freqs))

    def getNorm(self):
        mx = 10**self.scale

        if self.normScale == 'linear':
            mn = 0.0
            norm = pltLinNorm(mn, mx)

        elif self.normScale == 'log':
            mn = 1e-10
            norm = pltLogNorm(mn, mx)

        else:
            raise RuntimeError('Invalid norm %s.' % norm)

        return norm

    def updatePlot(self, event=None):
        """Draw the spectrogram plot.
        """
        if self.needsFirstPlot:
            self.firstPlot()

        else:
            cap = self.getCap()
            freqs, powers = self.getSpectrum(cap)

            #self.updateNorm(powers)

            self.canvas.restore_region(self.background)
            self.wimg.set_array(powers[:, :, 0].T)
            self.wimg.set_extent(self.getExtent(cap, freqs))
            self.ax.draw_artist(self.wimg)

            ##self.cbAx.draw_artist(self.cbar.patch)
            ##self.cbAx.draw_artist(self.cbar.solids)

            #self.cbar.draw_all()
            #self.canvas.blit(self.cbAx.bbox)

            #self.canvas.blit(self.fig.bbox)
            self.canvas.blit(self.ax.bbox)

            # for debugging, redraws everything
            ##self.canvas.draw()

    def captureImage(self, event=None):
        ## Parts borrowed from backends_wx.py from matplotlib
        # Fetch the required filename and file type.
        filetypes, exts, filter_index = self.canvas._get_imagesave_wildcards()
        default_file = self.canvas.get_default_filename()
        dlg = wx.FileDialog(self, "Save to file", "", default_file, filetypes,
                            wx.SAVE | wx.OVERWRITE_PROMPT)
        dlg.SetFilterIndex(filter_index)
        if dlg.ShowModal() == wx.ID_OK:
            dirname = dlg.GetDirectory()
            filename = dlg.GetFilename()
            format = exts[dlg.GetFilterIndex()]
            basename, ext = os.path.splitext(filename)
            if ext.startswith('.'):
                ext = ext[1:]
            if ext in ('svg', 'pdf', 'ps', 'eps', 'png') and format != ext:
                #looks like they forgot to set the image type drop
                #down, going with the extension.
                format = ext
            self.canvas.print_figure(os.path.join(dirname, filename),
                                     format=format)
Пример #2
0
class BasePlotWindow(VTKPlotWindowGUI.VTKPlotWindowGUI):

    def CreateLowerPanel(self):
        return PlotInfoGUIC.PlotInfoGUIC(self)

    def EnableToolbar(self):
        self.toolbar.EnableTool(self.toolbar.AUTO_THRESHOLD, False)
        self.toolbar.EnableTool(self.toolbar.VIEW_SYMBOLS, True)
        self.toolbar.EnableTool(self.toolbar.COPY_HIGHLIGHT, False)
        self.toolbar.EnableTool(self.toolbar.SHOW_HIGHLIGHT, False)

    def __del__(self):
        gsm = component.getGlobalSiteManager()
        gsm.unregisterHandler(self.OnImageChangeEvent)

    def __init__(self, parent, *args, **kw):

        VTKPlotWindowGUI.VTKPlotWindowGUI.__init__(self, parent)

        # ------------------------------------------------------------------------------------
        # Set up some zope event handlers
        # ------------------------------------------------------------------------------------
        component.provideHandler(self.OnImageChangeEvent)

        self._voxel_volume_set = False
        self._voxel_volume = 1.0
        if ('bar' in kw):
            self._usebar = True
        else:
            self._usebar = False

        self._scale = 1.0
        self._unit = kw.get('units', 'pixels')

        self._dragging = 0                       # is this needed?
        self._x0 = None
        self._y0 = None
        self._x1 = None
        self._y1 = None
        self._select_x0 = None
        self._select_x1 = None
        self._select_i0 = None
        self._select_i1 = None
        self.xdata = []
        self.ydata = []
        self._line = [None, None]
        self._liney = None
        self._title = "This is the title"
        self.filename = ''
        self._xlabel = ""
        self._ylabel = ""
        self._usesymbols = False
        # indicates whether nearest data point should be highlighted or not
        self._use_highlight_data = True
        self._isROI = False
        self._linelength = None
        self._inputname = ""
        self._highlight_visible = False
        self.__shortname__ = 'VTKPlot'
        self._otsu_threshold = None
        self._otsu_marker = None

        self._unit_scalings = {'pixels': 1.0, 'mm': 1.0, 'wavelength': 1.0}
        self.plot_data = None
        self.legend = None

        if ('scale' in kw):
            self._scale = float(kw['scale'])
        if ('title' in kw):
            self._title = kw['title']
        if ('xlabel' in kw):
            self._xlabel = kw['xlabel']
        if ('ylabel' in kw):
            self._ylabel = kw['ylabel']

        # get icon factory
        self._stockicons = StockItems.StockIconFactory()

        # create info panel - it'll depend on whether this is a line plot or
        # histogram window
        self.lower_panel = self.CreateLowerPanel()
        self.GetSizer().Add(self.lower_panel, 0, wx.EXPAND, 5)

        # create a matplotlib panel
        self.dpi = 100
        self.fig = Figure((3.0, 3.0), dpi=self.dpi)
#        self.fig.subplots_adjust(left=0.07, right=0.97, bottom=0.08, top=0.95)
        self.fig.subplots_adjust(left=0.0, right=1, bottom=0.0, top=1)

        self.axes = self.fig.add_subplot(111)

        # default labels
        self.axes.set_title(self._title, size=10)
        self.axes.set_ylabel(self._ylabel, size=8)

        self.line_tool = None    # A red, horizontal line tool
        self.end_markers = None  # Markers that go at the end of each line
        self.data_markers = None  # Marks that indicate nearest data point

        pylab.setp(self.axes.get_xticklabels(), fontsize=8)
        pylab.setp(self.axes.get_yticklabels(), fontsize=8)

        sizer = wx.BoxSizer(wx.VERTICAL)

        # create canvas for plot widget
        self.canvas = FigCanvas(self.m_panelMatplotPanel, -1, self.fig)

        self.m_panelMatplotPanel.SetSizer(sizer)
        self.m_panelMatplotPanel.Fit()

        # activate interactive navigation
        self.toolbar = MicroViewNavigationToolbar(self.canvas)
        self.toolbar.Realize()

        tw, th = self.toolbar.GetSizeTuple()
        fw, fh = self.canvas.GetSizeTuple()
        self.toolbar.SetSize(wx.Size(fw, th))

        sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND)
        sizer.Add(self.canvas, 1, wx.EXPAND)
        self.toolbar.update()

        # adjust toolbar
        self.EnableToolbar()

        # wire up events
        self.canvas.mpl_connect('motion_notify_event', self.MouseMoveEvent)
        self.canvas.mpl_connect('key_press_event', self.KeyPressEvent)
        self.canvas.mpl_connect('figure_leave_event', self.LeaveCanvasEvent)

        # wire up events here
        self.toolbar.Bind(
            wx.EVT_TOOL, self.SaveData, id=self.toolbar.SAVE_DATA)
        self.toolbar.Bind(
            wx.EVT_TOOL, self.SaveSnapShot, id=self.toolbar.SAVE_SNAPSHOT)
        self.toolbar.Bind(
            wx.EVT_TOOL, self.symbolsOnOff, id=self.toolbar.VIEW_SYMBOLS)
       # self.toolbar.Bind(wx.EVT_TOOL, self.Reset, id=self.toolbar.RESET_VIEW)
        self.toolbar.Bind(
            wx.EVT_TOOL, self.AutoThreshold, id=self.toolbar.AUTO_THRESHOLD)
        self.toolbar.Bind(
            wx.EVT_TOOL, self.onCopyHighlightToolbarButton, id=self.toolbar.COPY_HIGHLIGHT)
        self.toolbar.Bind(
            wx.EVT_TOOL, self.onShowHighlightToolbarToogle, id=self.toolbar.SHOW_HIGHLIGHT)
        self.toolbar.Bind(
            wx.EVT_TOOL, self.NearestDataSymbolsOnOff, id=self.toolbar.VIEW_NEARESTDATA)
        self.toolbar.Bind(
            wx.EVT_TOOL, self.select_roi, id=self.toolbar.SELECT_ROI)

        # listen to size events
        self.Bind(wx.EVT_PAINT, self.OnPaint)

        self._plotData = None

        # This table is used to make the input invisible
        self._wlTableInvisible = vtk.vtkWindowLevelLookupTable()
        self._wlTableInvisible.SetSaturationRange(0, 0)
        self._wlTableInvisible.SetHueRange(0, 0)
        self._wlTableInvisible.SetValueRange(0, 1)
        self._wlTableInvisible.SetNumberOfColors(2)
        self._wlTableInvisible.SetTableValue(0, 0.0, 0.0, 0.0, 0.0)
        self._wlTableInvisible.SetTableValue(1, 0.0, 0.0, 0.0, 0.0)
        self._wlTableInvisible.Build()

        # Invoke an event to react to currently loaded image
        mv = component.getUtility(IMicroViewMainFrame)
        current_image = mv.GetCurrentImageIndex()
        number_images_displayed = mv.GetNumberOfImagesCurrentlyLoaded()
        title = mv.GetCurrentImageTitle()
        self.OnImageChangeEvent(CurrentImageChangeEvent(
            current_image, number_images_displayed, title))

    def OnPaint(self, evt):

        s = self.GetClientSize()

        try:
            self.fig.tight_layout(pad=0.25)
        except ValueError:
            # turn off legend
            if self.legend:
                self.legend.set_visible(False)
            return

        if (s[0] < 350) or (s[1] < 350):
            # turn off legend
            if self.legend:
                self.legend.set_visible(False)
        else:
            if self.legend:
                self.legend.set_visible(True)

        self.canvas.draw()

    def ClearPlot(self):

        self.xdata = np.array([])
        self.ydata = np.array([])

        if self.plot_data:
            for p in self.plot_data:
                p.remove()
        if self.end_markers:
            self.end_markers.remove()
        if self.data_markers:
            self.data_markers.remove()

        self.HideOtsuThreshold()
        self.plot_data = self.end_markers = self.data_markers = None

        # redraw canvas
        self.canvas.draw()

    def SetFileName(self, filename):
        self.filename = filename

    def update_axes_bounds(self):

        xmax = round(self.xdata.max(), 0) + 1
        xmin = 0

        ymin = round(self.ydata.min(), 0) - 1
        ymax = round(self.ydata.max(), 0) + 1

        self.axes.set_xbound(lower=xmin, upper=xmax)
        self.axes.set_ybound(lower=ymin, upper=ymax)

    def update_colour_names(self):

        numC = 1
        if len(self.ydata.shape) > 1:
            numC = self.ydata.shape[-1]
            self.colours = ['red', 'green', 'blue', 'black']
        else:
            self.ydata.shape = (self.ydata.shape[0], 1)
            self.colours = ['black']

    def update_channel_names(self):

        numC = 1
        if len(self.ydata.shape) > 1:
            numC = self.ydata.shape[-1]
            self.channel_names = ['red', 'green', 'blue', 'opacity']
        else:
            self.ydata.shape = (self.ydata.shape[0], 1)
            self.channel_names = ['chan. 1']

    def draw_plot(self):
        """ Redraws the plot"""

        # set axis range
        self.update_axes_bounds()
        self.update_colour_names()
        self.update_channel_names()

        self.axes.grid(True, color='gray')
        pylab.setp(self.axes.get_xticklabels(), visible=True)

        numC = 1
        if len(self.ydata.shape) > 1:
            numC = self.ydata.shape[-1]
        else:
            self.ydata.shape = (self.ydata.shape[0], 1)

        self.file_label = '# Pos. (%s)\t' % self._unit + \
            (numC - 1) * '%s\t' + '%s' + '\n#'
        self.file_label = self.file_label % tuple(self.channel_names[:numC])

        args = []
        marker_x = []
        marker_y = []
        marker_c = []

        for i in range(numC):
            args.append(self.xdata)
            args.append(self.ydata[:, i])
            args.append(self.colours[i])
            marker_x.append(self.xdata[0])
            marker_y.append(self.ydata[0, i])
            marker_c.append('b')
            marker_x.append(self.xdata[-1])
            marker_y.append(self.ydata[-1, i])
            marker_c.append('g')

            if self.plot_data:
                if len(self.plot_data) != numC:
                    for p in self.plot_data:
                        p.remove()
                    self.plot_data = None

            if self.plot_data is None:
                self.plot_data = self.axes.plot(*args, linewidth=1)
                self.legend = self.axes.legend(
                    tuple(self.channel_names[0:numC]))
                pylab.setp(self.legend.get_texts(), fontsize='x-small')
            else:
                for i in range(numC):
                    self.plot_data[i].set_xdata(args[i * 3 + 0])
                    self.plot_data[i].set_ydata(args[i * 3 + 1])

        # draw end markers
        if self.end_markers:
            self.end_markers.remove()
        self.end_markers = self.axes.scatter(
            marker_x, marker_y, c=tuple(marker_c), linewidths=0, zorder=3)

    @component.adapter(CurrentImageChangeEvent)
    def OnImageChangeEvent(self, evt):

        # I only care about the input image number - I need to make a context switch here to account
        # for multiple ROIs
        self._current_index = evt.GetCurrentImageIndex()

    def showThresholdMarker(self, threshold):

        dlg = wx.MessageDialog(self, 'threshold:' + ' %0.1f' % (
            threshold), 'Auto-Threshold', wx.OK | wx.ICON_INFORMATION)
        dlg.ShowModal()
        dlg.Destroy()

        if self._usebar is True:
            self._otsu_threshold = threshold
            # draw Otsu threshold
            if self._otsu_marker:
                self._otsu_marker.remove()
            self._otsu_marker = self.axes.scatter([self._otsu_threshold], [-1000], c='r',
                                                  marker='^', linewidths=0, zorder=3)

    def GetMouseMoveLabelFormat(self):
        return 'x=%11.5f, y=%s'

    def MouseMoveEvent(self, evt):

        refresh_required = False

        # erase current data markers
        if self.data_markers:
            self.data_markers.remove()
            self.data_markers = None
            refresh_required = True

        # determine nearest data values and update wx gui
        if evt.xdata and len(self.xdata) > 0:
            index = abs(self.xdata - evt.xdata).argmin()
            val = self.ydata[index, :]
            marker_x = [self.xdata[index]] * len(val)
            marker_y = val
            marker_c = ['red'] * len(val)
            if len(val) == 1:
                val = val[0]
            else:
                val = tuple(val)

            # update line/histogram
            if evt.button == 2 or (evt.button == 1 and self.toolbar._active == 'SELECT_ROI'):
                self.setLinePoint(1, evt)

            fmt = self.GetMouseMoveLabelFormat()

            self.lower_panel.m_staticTextNearestDataValue.SetLabel(
                fmt % (self.xdata[index], str(val)))

            # optionally draw highlighted data
            if self._use_highlight_data and self.toolbar.ShouldIShowCursor():
                # draw data markers
                self.data_markers = self.axes.scatter(
                    marker_x, marker_y, c=marker_c, linewidths=0, zorder=4)
                refresh_required = True

        else:
            self.lower_panel.m_staticTextNearestDataValue.SetLabel('')

        if refresh_required:
            self.canvas.draw()

        # update wx user interface
        self.reportPosition([evt.xdata, evt.ydata])
        self.reportLineLength()

    def SetSelectionRange(self, x0, x1):
        self._select_x0 = x0
        self._select_x1 = x1
        self.NotifyHistoHighlightChange()

    def GetSelectionRange(self):
        return self._select_x0, self._select_x1

    def KeyPressEvent(self, evt):

        k = evt.key

        if k == '1':
            self.setFirstLinePoint(evt)
        elif k == '2':
            self.setSecondLinePoint(evt)
        elif k == 'r':
            self.Reset()
        elif k == 'y':
            self.removeMeasurementLine()

    def LeaveCanvasEvent(self, evt):
        # remove marker
        if self.data_markers:
            self.data_markers.remove()
            self.data_markers = None
        self.canvas.draw()

    def SetBinSize(self, evt=None, binsize=None):
        if binsize is None:
            binsize = float(self.binVar.get())
        else:
            self.binVar.set(str(binsize))
        # generate an event
        event.notify(BinSizeModifiedEvent(binsize))

    def Delete(self):
        self.Hide()

    def Hide(self, evt=None):

        self.removeMeasurementLine()
        if self._usebar is True:
            if self._isROI is False:
                self.HideHighlight()
            self.HideOtsuThreshold()

    def HideHighlight(self, image_index=None):

        if self._isROI is True:
            self._isROI = False
            event.notify(HistogramClosedEvent())

        if self._usebar is True and self.GetHighlightVisible() is True:
            self.SetHighlightVisible(False)

        self.NotifyHistoHighlightChange()
        self.OrthoPlanesRemoveInput('histogram')

    def HideOtsuThreshold(self):
        if self._otsu_marker:
            self._otsu_marker.remove()
            self._otsu_marker = None

    def OrthoPlanesRemoveInput(self, name):
        event.notify(OrthoPlanesRemoveInputEvent(name))

    def Reset(self, e=None):

        self.removeMeasurementLine()

        # this might need to be reworked
        self.lower_panel.m_staticTextLineLength.SetLabel('')

    def GetCurrentDirectory(self):

        # determine working directory
        curr_dir = cwd = os.getcwd()
        config = MicroViewSettings.MicroViewSettings.getObject()

        # over-ride with system-wide directory
        try:
            curr_dir = config.GlobalCurrentDirectory or curr_dir
        except:
            config.GlobalCurrentDirectory = curr_dir

        try:
            curr_dir = config.CurrentSnapshotDirectory or curr_dir
        except:
            config.CurrentSnapshotDirectory = curr_dir

        return curr_dir

    def SaveCurrentDirectory(self, curr_dir):
        config = MicroViewSettings.MicroViewSettings.getObject()
        config.CurrentSnapshotDirectory = curr_dir

    def GetwlTableInvisible(self):
        return self._wlTableInvisible

    def CreateHighlightLookupTable(self):

        # Create colour table for histogram overlay
        table = vtk.vtkWindowLevelLookupTable()
        table.SetSaturationRange(0, 0)
        table.SetHueRange(0, 0)
        table.SetValueRange(0, 1)
        table.SetLevel(0.5)
        table.SetWindow(1.0)
        table.SetNumberOfColors(2)
        table.SetTableValue(0, 0.0, 0.0, 0.0, 0.0)
        table.SetTableValue(1, 1.0, 0.0, 0.0, 1.0)
        table.Build()
        return table

    def GetInputName(self):
        return self._inputname

    def SetInputName(self, name):
        self._inputname = name

    def NotifyHistoHighlightChange(self):
        event.notify(HistogramSelectionModifiedEvent(
            self._select_x0, self._select_x1, self.GetHighlightVisible()))

    def SelectionToROI(self):
        """Assert ourselves as the owner of the ROI object for this image"""

        # This next line is increasingly not accurate - need something better
        self._isROI = True

        # send out a notification to allow any ROI visuals to modify themselves
        event.notify(HistogramIsActiveROIEvent())

    def GetROIType(self, index):
        return 'custom'

    def AutoThreshold(self, evt):
        """Force an autothreshold calculation

        This routine posts an AutoThresholdCommandEvent command in order to request a new calculation of an
        optimal Otsu threshold.
        """
        event.notify(AutoThresholdCommandEvent())

    def SetInput(self, inp):

        if self.plot_data:
            for p in self.plot_data:
                p.remove()
            self.plot_data = None

        if self.data_markers:
            self.data_markers.remove()
            self.data_markers = None

        self._plotData = inp

        # bit of a bad name -- this next line actually resets all inputs, then
        # adds self._plotData
        self.SetHighlightVisible(False)

    def SaveSnapShot(self, evt):
        # Fetch the required filename and file type.
        filetypes, exts, filter_index = self.canvas._get_imagesave_wildcards()
        default_file = "image." + self.canvas.get_default_filetype()
        dlg = wx.FileDialog(self, "Save snapshot to file", "", default_file,
                            filetypes,
                            wx.SAVE | wx.OVERWRITE_PROMPT)
        dlg.SetFilterIndex(filter_index)
        if dlg.ShowModal() == wx.ID_OK:
            dirname = dlg.GetDirectory()
            filename = dlg.GetFilename()
            format = exts[dlg.GetFilterIndex()]
            basename, ext = os.path.splitext(filename)
            if ext.startswith('.'):
                ext = ext[1:]
            if ext in ('svg', 'pdf', 'ps', 'eps', 'png') and format != ext:
                # looks like they forgot to set the image type drop
                # down, going with the extension.
                dlg = wx.MessageDialog(self, 'extension %s did not match the selected image type %s; going with %s' %
                                             (ext, format, ext), 'Warning', wx.OK | wx.ICON_WARNING)
                dlg.ShowModal()
                dlg.Destroy()
                format = ext
            try:
                self.canvas.print_figure(
                    os.path.join(dirname, filename), format=format)
            except Exception, e:
                logging.error(e.message)