コード例 #1
0
    def loadMap(self, rec):
        self.getElement('Canvas').clear()
        self.currentMap = Map(self, rec)
        self.currentMap.loadStubs()
        self.currentMap.sPlotItem.sigClicked.connect(self.mapPointClicked)

        self.getElement('Canvas').addGraphicsItem(self.currentMap.sPlotItem,
                                                  movable=False)

        self.invalidate()
        self.loadFromDB()
        self.update()
コード例 #2
0
ファイル: MapAnalyzer.py プロジェクト: ablot/acq4
    def loadMap(self, rec):
        self.getElement('Canvas').clear()
        self.currentMap = Map(self, rec)
        self.currentMap.loadStubs()
        self.currentMap.sPlotItem.sigClicked.connect(self.mapPointClicked)

        self.getElement('Canvas').addGraphicsItem(self.currentMap.sPlotItem, movable=False)
        
        self.invalidate()
        self.loadFromDB()
        self.update()
コード例 #3
0
ファイル: MapAnalyzer.py プロジェクト: ablot/acq4
class MapAnalyzer(AnalysisModule):
    dbIdentity = 'MapAnalyzer'
    def __init__(self, host):
        AnalysisModule.__init__(self, host)
        if self.dataModel is None:
            raise Exception("MapAnalyzer module requires a data model, but none is loaded yet.")
        
        self.currentMap = None
        self.analysisValid = False
        self.colorsValid = False
        
        self.ctrlLayout = pg.LayoutWidget()
        self.ctrl = ptree.ParameterTree(showHeader=False)
        self.ctrlLayout.addWidget(self.ctrl, row=0, col=0)
        self.recalcBtn = QtGui.QPushButton('Recalculate')
        self.ctrlLayout.addWidget(self.recalcBtn, row=1, col=0)
        self.storeBtn = pg.FeedbackButton('Store to DB')
        self.ctrlLayout.addWidget(self.storeBtn, row=2, col=0)
        
        
        self.loader = Loader(host=self, dm=host.dataManager())
        
        
        modPath = os.path.abspath(os.path.dirname(__file__))
        self.colorMapper = ColorMapper(filePath=os.path.join(modPath, "colorMaps"))
        self._elements_ = OrderedDict([
            ('File Loader', {'type': 'fileInput', 'size': (300, 300), 'host': self, 'showFileTree': False}),
            ('Map Loader', {'type': 'ctrl', 'object': self.loader, 'size': (300, 300), 'pos': ('below', 'File Loader')}),
            ('Color Mapper', {'type':'ctrl', 'object': self.colorMapper, 'size': (800,200), 'pos': ('right', 'Map Loader')}),
            ('Canvas', {'type': 'canvas', 'size': (800, 400), 'pos':('right', 'Color Mapper'), 'args': {'name': 'MapAnalyzer'}}),
            ('Options', {'type': 'ctrl', 'object': self.ctrlLayout, 'size': (300, 500), 'pos': ('bottom', 'Map Loader')}),
            ('Data Plot', {'type': 'plot', 'pos': ('top', 'Color Mapper'), 'size': (800, 300)}),
            ('Score Histogram', {'type': 'plot', 'pos': ('bottom', 'Data Plot'), 'size': (800, 300)}),
            ('Timeline', {'type': 'plot', 'pos': ('bottom', 'Data Plot'), 'size': (800, 300)}),
            ('Stats Table', {'type': 'dataTree', 'pos': ('bottom', 'Canvas'), 'size': (800,300)}),
        ])
        host.resize(1100, 800)
        self.initializeElements()
        
        
        self.filterStage = EventFilter()
        self.spontRateStage = SpontRateAnalyzer(plot=self.getElement('Timeline', create=True))
        self.statsStage = EventStatisticsAnalyzer(histogramPlot=self.getElement('Score Histogram', create=True))
        self.regions = RegionMarker(self.getElement('Canvas', create=True))
        self.stages = [self.filterStage, self.spontRateStage, self.statsStage, self.regions]
        
        params = [
            dict(name='Time Ranges', type='group', children=[
                dict(name='Direct Start', type='float', value=0.498, suffix='s', step=0.001, siPrefix=True),
                dict(name='Stimulus', type='float', value=0.5, suffix='s', step=0.001, siPrefix=True),
                dict(name='Post Start', type='float', value=0.502, suffix='s', step=0.001, siPrefix=True),
                dict(name='Post Stop', type='float', value=0.700, suffix='s', step=0.001, siPrefix=True),
            ]),
        ]
        
        ## each processing stage comes with its own set of parameters
        for stage in self.stages:
            params.append(stage.parameters())
        
        self.params = ptree.Parameter.create(name='options', type='group', children=params)
        self.ctrl.setParameters(self.params, showTop=False)
        
        canvas = self.getElement('Canvas', create=True)
        #self.scalebar = pg.ScaleBar(100e-6)
        #canvas.addGraphicsItem(self.scalebar, name="ScaleBar")
        self.scalebar = pg.ScaleBar(size=500e-6)
        self.scalebar.setParentItem(canvas.view)
        self.scalebar.anchor((1, 1), (1, 1), offset=(-20, -20))
        
        ## Note: need to reconnect this!!
        #self.params.sigTreeStateChanged.connect(self.invalidate)
        self.recalcBtn.clicked.connect(self.recalcClicked)
        self.storeBtn.clicked.connect(self.storeToDB)
        self.params.param('Time Ranges').sigTreeStateChanged.connect(self.updateTimes)
        
        self.getElement('Color Mapper', create=True).sigChanged.connect(self.colorMapChanged)
        self.regions.sigRegionChanged.connect(self.processRegions)
        
    def elementChanged(self, element, old, new):
        name = element.name()

    def loadMap(self, rec):
        self.getElement('Canvas').clear()
        self.currentMap = Map(self, rec)
        self.currentMap.loadStubs()
        self.currentMap.sPlotItem.sigClicked.connect(self.mapPointClicked)

        self.getElement('Canvas').addGraphicsItem(self.currentMap.sPlotItem, movable=False)
        
        self.invalidate()
        self.loadFromDB()
        self.update()
                
    def loadScan(self, dh):
        ## called by Map objects to load scans
        scans = Scan.loadScanSequence(dh, self)
        #if len(scans) > 1:
            #raise Exception("Scan sequences not supported yet.")
        for scan in scans:
            ci = scan.canvasItem()
            self.getElement('Canvas').addItem(ci)
            ci.hide()
            scan.canvasItem().graphicsItem().sigClicked.connect(self.scanPointClicked)

        return scans
        
    def loadScanFromDB(self, sourceDir):
        ## Called by Scan as it is loading
        statTable = self.loader.dbGui.getTableName('Photostim.sites')
        eventTable = self.loader.dbGui.getTableName('Photostim.events')
        db = self.loader.dbGui.getDb()
        stats = db.select(statTable, '*', where={'ProtocolSequenceDir': sourceDir})
        events = db.select(eventTable, '*', where={'ProtocolSequenceDir': sourceDir}, toArray=True)
        return events, stats
        
    def loadSpotFromDB(self, sourceDir):
        ## Called by Scan as it is loading single points
        statTable = self.loader.dbGui.getTableName('Photostim.sites')
        eventTable = self.loader.dbGui.getTableName('Photostim.events')
        db = self.loader.dbGui.getDb()
        stats = db.select(statTable, '*', where={'ProtocolDir': sourceDir})
        events = db.select(eventTable, '*', where={'ProtocolDir': sourceDir}, toArray=True)
        return events, stats
        
    def loadFileRequested(self, fhList):
        canvas = self.getElement('Canvas')
        model = self.dataModel

        with pg.ProgressDialog("Loading data..", 0, len(fhList)) as dlg:
            for fh in fhList:
                try:
                    ## TODO: use more clever detection of Scan data here.
                    if fh.isFile() or model.dirType(fh) == 'Cell':
                        canvas.addFile(fh, movable=False)
                    #else:
                        #self.loadScan(fh)
                        return True
                    else:
                        return False
                except:
                    debug.printExc("Error loading file %s" % fh.name())
                    return False
                dlg += 1
                if dlg.wasCancelled():
                    return
        

    def getDb(self):
        db = self.loader.dbGui.getDb()
        return db
        
        
    def getColor(self, stats, data):
        ## return the color to represent this data
        
        ## merge data together
        d2 = data.copy()
        del d2['sites']
        d2 = OrderedDict(d2)
        d2.update(stats)
        del d2['ProtocolDir']
        
        mapper = self.getElement('Color Mapper')
        mapper.setArgList(d2.keys())
        
        return mapper.getColor(d2)
        
    def colorMapChanged(self):
        self.colorsValid = False
        self.update()
        
    def update(self):
        if not self.analysisValid:
            print "Updating analysis.."
            map = self.currentMap
            if map is None:
                return
            scans = map.scans
            
            ## Get a list of all stimulations in the map and their times.
            sites = []
            for s in scans:
                sites.extend(s.getTimes())
            sites.sort(key=lambda i: i[1])
            
            ## get list of all events
            events = []
            for scan in scans:
                ev = scan.getAllEvents()
                if ev is not None:
                    events.append(ev.copy())
            
            ## set up table of per-stimulation data
            spontRates = np.zeros(len(sites), dtype=[('ProtocolDir', object), ('start', float), ('stop', float), ('spontRate', float), ('filteredSpontRate', float)])
            spontRates[:] = [s+(0,0) for s in sites] ## fill with data
            
            filtered = None
            if len(events) > 0:
                events = np.concatenate(events)
                filtered = self.filterStage.process(events)
            
                ## compute spontaneous rates
                sr = self.spontRateStage.process(spontRates, filtered)
                spontRates['spontRate'] = sr['spontRate']
                spontRates['filteredSpontRate'] = sr['filteredSpontRate']
            else:
                sr = {'ampMean': 0, 'ampStdev': 0}
            
            output = self.statsStage.process(map, spontRates, filtered, sr['ampMean'], sr['ampStdev'])
            self.analysisValid = True
            
        if not self.colorsValid:
            self.currentMap.recolor()
            self.colorsValid = True
        
    def invalidate(self):
        print "invalidate."
        self.analysisValid = False
        self.colorsValid = False

    def recalcClicked(self):
        self.invalidate()
        self.update()
        
    def updateTimes(self):
        self.params['Spontaneous Rate', 'Stop Time'] = self.params['Time Ranges', 'Direct Start']
        self.params['Analysis Methods', 'Stimulus Time'] = self.params['Time Ranges', 'Stimulus']
        self.params['Analysis Methods', 'Pre Stop'] = self.params['Time Ranges', 'Direct Start']
        self.params['Analysis Methods', 'Post Start'] = self.params['Time Ranges', 'Post Start']
        self.params['Analysis Methods', 'Post Stop'] = self.params['Time Ranges', 'Post Stop']
        
        
    def scanPointClicked(self, gitem, points):
        plot = self.getElement('Data Plot', create=True)
        plot.clear()
        scan = gitem.scan
        scan.displayData(points[0].data(), plot, 'w')
        
    def mapPointClicked(self, gitem, points):
        plot = self.getElement('Data Plot', create=True)
        plot.clear()
        data = []
        for p in points:
            for source in p.data()['sites']:
                data.append(source)
            #data.extend(p.data)
        for i in range(len(data)):
            scan, fh = data[i]
            scan.displayData(fh, plot, pen=(i, len(data)*1.3), eventFilter=self.filterStage.process)
        
        self.getElement('Stats Table').setData(points[0].data())

    def storeToDB(self):
        try:
            self.update()
            
            ## Determine currently selected table to store to
            dbui = self.getElement('Map Loader').dbGui
            identity = self.dbIdentity+'.sites'
            mapTable = dbui.getTableName('Photostim.maps')
            table = dbui.getTableName(identity)
            db = dbui.getDb()

            if db is None:
                raise Exception("No DB selected")
            
            fields = OrderedDict([
                ('Map', {'Type': 'int', 'Link': mapTable}),
                #('CellDir', 'directory:Cell'),
                ('FirstSite', 'directory:Protocol'),
                ('Sites', 'blob'),
                ('PoissonScore', 'real'),
                ('PoissonScore_Pre', 'real'),
                ('PoissonAmpScore', 'real'),
                ('PoissonAmpScore_Pre', 'real'),
                ('HasInput', 'int'),
                ('FirstLatency', 'real'),
                ('ZScore', 'real'),
                ('FitAmpSum', 'real'),
                ('FitAmpSum_Pre', 'real'),
                ('NumEvents', 'real'),
                ('SpontRate', 'real'),
                ('DirectPeak', 'real'),
                ('Region', 'text'),
            ])
            
            mapRec = self.currentMap.getRecord()
            data = []
            for spot in self.currentMap.spots:
                rec = {}
                for k in fields:
                    if k in spot['data']:
                        rec[k] = spot['data'][k]
                #rec['CellDir'] = mapRec['cell'] 
                rec['Map'] = self.currentMap.rowID
                sites = [s[1] for s in spot['data']['sites']]
                rec['FirstSite'] = sites[0]
                rec['Sites'] = [db.getDirRowID(s) for s in sites]
                data.append(rec)
                
            
            with db.transaction():
                ## Make sure target table exists and has correct columns, links to input file
                db.checkTable(table, owner=identity, columns=fields, create=True, addUnknownColumns=True, indexes=[['Map']])
                
                # delete old
                db.delete(table, where={'Map': self.currentMap.rowID})

                # write new
                with pg.ProgressDialog("Storing map data...", 0, 100) as dlg:
                    for n, nmax in db.iterInsert(table, data, chunkSize=100):
                        dlg.setMaximum(nmax)
                        dlg.setValue(n)
                        if dlg.wasCanceled():
                            raise HelpfulException("Scan store canceled by user.", msgType='status')
            self.storeBtn.success()
        except:
            self.storeBtn.failure()            
            raise
        
    def processRegions(self):
        ## Compute regions for each spot
        for spot in self.currentMap.spots:
            dh = spot['data']['sites'][0][1]
            pos = spot['pos']
            rgn = self.regions.getRegion(pos)
            spot['data']['Region'] = rgn
            #print dh,rgn
            
        ## Store ROI positions with cell
        cell = self.currentMap.getRecord()['cell'] 
        rgns = self.regions.getRegions()
        cell.setInfo(MapAnalyzer_Regions=rgns)
        
    def loadFromDB(self):
        ## read in analysis from DB
        
        dbui = self.getElement('Map Loader').dbGui
        identity = self.dbIdentity+'.sites'
        mapTable = dbui.getTableName('Photostim.maps')
        table = dbui.getTableName(identity)
        db = dbui.getDb()

        if db is None:
            raise Exception("No DB selected")
        if not db.hasTable(table):
            return None
        
        fields = OrderedDict([
            ('Map', {'Type': 'int', 'Link': mapTable}),
            #('Sites', 'blob'),
            ('PoissonScore', 'real'),
            ('PoissonScore_Pre', 'real'),
            ('PoissonAmpScore', 'real'),
            ('PoissonAmpScore_Pre', 'real'),
            ('HasInput', 'int'),
            ('FirstLatency', 'real'),
            ('ZScore', 'real'),
            ('FitAmpSum', 'real'),
            ('FitAmpSum_Pre', 'real'),
            ('NumEvents', 'real'),
            ('SpontRate', 'real'),
            ('DirectPeak', 'real'),
            ('Region', 'text'),
        ])
        
        #mapRec = self.currentMap.getRecord()
        recs = db.select(table, ['rowid', '*'], where={'Map': self.currentMap.rowID})
        if len(recs) == len(self.currentMap.spots):
            for i, spot in enumerate(self.currentMap.spots):
                
                for k in fields.keys() + recs[i].keys():
                    spot['data'][k] = recs[i].get(k, None)
            self.analysisValid = True
            print "reloaded analysis from DB", self.currentMap.rowID
        else:
            print "analysis incomplete:", len(recs), len(self.currentMap.spots)
コード例 #4
0
class MapAnalyzer(AnalysisModule):
    dbIdentity = 'MapAnalyzer'

    def __init__(self, host):
        AnalysisModule.__init__(self, host)
        if self.dataModel is None:
            raise Exception(
                "MapAnalyzer module requires a data model, but none is loaded yet."
            )

        self.currentMap = None
        self.analysisValid = False
        self.colorsValid = False

        self.ctrlLayout = pg.LayoutWidget()
        self.ctrl = ptree.ParameterTree(showHeader=False)
        self.ctrlLayout.addWidget(self.ctrl, row=0, col=0)
        self.recalcBtn = QtGui.QPushButton('Recalculate')
        self.ctrlLayout.addWidget(self.recalcBtn, row=1, col=0)
        self.storeBtn = pg.FeedbackButton('Store to DB')
        self.ctrlLayout.addWidget(self.storeBtn, row=2, col=0)

        self.loader = Loader(host=self, dm=host.dataManager())

        modPath = os.path.abspath(os.path.dirname(__file__))
        self.colorMapper = ColorMapper(
            filePath=os.path.join(modPath, "colorMaps"))
        self._elements_ = OrderedDict([
            ('File Loader', {
                'type': 'fileInput',
                'size': (300, 300),
                'host': self,
                'showFileTree': False
            }),
            ('Map Loader', {
                'type': 'ctrl',
                'object': self.loader,
                'size': (300, 300),
                'pos': ('below', 'File Loader')
            }),
            ('Color Mapper', {
                'type': 'ctrl',
                'object': self.colorMapper,
                'size': (800, 200),
                'pos': ('right', 'Map Loader')
            }),
            ('Canvas', {
                'type': 'canvas',
                'size': (800, 400),
                'pos': ('right', 'Color Mapper'),
                'args': {
                    'name': 'MapAnalyzer'
                }
            }),
            ('Options', {
                'type': 'ctrl',
                'object': self.ctrlLayout,
                'size': (300, 500),
                'pos': ('bottom', 'Map Loader')
            }),
            ('Data Plot', {
                'type': 'plot',
                'pos': ('top', 'Color Mapper'),
                'size': (800, 300)
            }),
            ('Score Histogram', {
                'type': 'plot',
                'pos': ('bottom', 'Data Plot'),
                'size': (800, 300)
            }),
            ('Timeline', {
                'type': 'plot',
                'pos': ('bottom', 'Data Plot'),
                'size': (800, 300)
            }),
            ('Stats Table', {
                'type': 'dataTree',
                'pos': ('bottom', 'Canvas'),
                'size': (800, 300)
            }),
        ])
        host.resize(1100, 800)
        self.initializeElements()

        self.filterStage = EventFilter()
        self.spontRateStage = SpontRateAnalyzer(
            plot=self.getElement('Timeline', create=True))
        self.statsStage = EventStatisticsAnalyzer(
            histogramPlot=self.getElement('Score Histogram', create=True))
        self.regions = RegionMarker(self.getElement('Canvas', create=True))
        self.stages = [
            self.filterStage, self.spontRateStage, self.statsStage,
            self.regions
        ]

        params = [
            dict(name='Time Ranges',
                 type='group',
                 children=[
                     dict(name='Direct Start',
                          type='float',
                          value=0.498,
                          suffix='s',
                          step=0.001,
                          siPrefix=True),
                     dict(name='Stimulus',
                          type='float',
                          value=0.5,
                          suffix='s',
                          step=0.001,
                          siPrefix=True),
                     dict(name='Post Start',
                          type='float',
                          value=0.502,
                          suffix='s',
                          step=0.001,
                          siPrefix=True),
                     dict(name='Post Stop',
                          type='float',
                          value=0.700,
                          suffix='s',
                          step=0.001,
                          siPrefix=True),
                 ]),
        ]

        ## each processing stage comes with its own set of parameters
        for stage in self.stages:
            params.append(stage.parameters())

        self.params = ptree.Parameter.create(name='options',
                                             type='group',
                                             children=params)
        self.ctrl.setParameters(self.params, showTop=False)

        canvas = self.getElement('Canvas', create=True)
        #self.scalebar = pg.ScaleBar(100e-6)
        #canvas.addGraphicsItem(self.scalebar, name="ScaleBar")
        self.scalebar = pg.ScaleBar(size=500e-6)
        self.scalebar.setParentItem(canvas.view)
        self.scalebar.anchor((1, 1), (1, 1), offset=(-20, -20))

        ## Note: need to reconnect this!!
        #self.params.sigTreeStateChanged.connect(self.invalidate)
        self.recalcBtn.clicked.connect(self.recalcClicked)
        self.storeBtn.clicked.connect(self.storeToDB)
        self.params.param('Time Ranges').sigTreeStateChanged.connect(
            self.updateTimes)

        self.getElement('Color Mapper',
                        create=True).sigChanged.connect(self.colorMapChanged)
        self.regions.sigRegionChanged.connect(self.processRegions)

    def elementChanged(self, element, old, new):
        name = element.name()

    def loadMap(self, rec):
        self.getElement('Canvas').clear()
        self.currentMap = Map(self, rec)
        self.currentMap.loadStubs()
        self.currentMap.sPlotItem.sigClicked.connect(self.mapPointClicked)

        self.getElement('Canvas').addGraphicsItem(self.currentMap.sPlotItem,
                                                  movable=False)

        self.invalidate()
        self.loadFromDB()
        self.update()

    def loadScan(self, dh):
        ## called by Map objects to load scans
        scans = Scan.loadScanSequence(dh, self)
        #if len(scans) > 1:
        #raise Exception("Scan sequences not supported yet.")
        for scan in scans:
            ci = scan.canvasItem()
            self.getElement('Canvas').addItem(ci)
            ci.hide()
            scan.canvasItem().graphicsItem().sigClicked.connect(
                self.scanPointClicked)

        return scans

    def loadScanFromDB(self, sourceDir):
        ## Called by Scan as it is loading
        statTable = self.loader.dbGui.getTableName('Photostim.sites')
        eventTable = self.loader.dbGui.getTableName('Photostim.events')
        db = self.loader.dbGui.getDb()
        stats = db.select(statTable,
                          '*',
                          where={'ProtocolSequenceDir': sourceDir})
        events = db.select(eventTable,
                           '*',
                           where={'ProtocolSequenceDir': sourceDir},
                           toArray=True)
        return events, stats

    def loadSpotFromDB(self, sourceDir):
        ## Called by Scan as it is loading single points
        statTable = self.loader.dbGui.getTableName('Photostim.sites')
        eventTable = self.loader.dbGui.getTableName('Photostim.events')
        db = self.loader.dbGui.getDb()
        stats = db.select(statTable, '*', where={'ProtocolDir': sourceDir})
        events = db.select(eventTable,
                           '*',
                           where={'ProtocolDir': sourceDir},
                           toArray=True)
        return events, stats

    def loadFileRequested(self, fhList):
        canvas = self.getElement('Canvas')
        model = self.dataModel

        with pg.ProgressDialog("Loading data..", 0, len(fhList)) as dlg:
            for fh in fhList:
                try:
                    ## TODO: use more clever detection of Scan data here.
                    if fh.isFile() or model.dirType(fh) == 'Cell':
                        canvas.addFile(fh, movable=False)
                        #else:
                        #self.loadScan(fh)
                        return True
                    else:
                        return False
                except:
                    debug.printExc("Error loading file %s" % fh.name())
                    return False
                dlg += 1
                if dlg.wasCanceled():
                    return

    def getDb(self):
        db = self.loader.dbGui.getDb()
        return db

    def getColor(self, stats, data):
        ## return the color to represent this data

        ## merge data together
        d2 = data.copy()
        del d2['sites']
        d2 = OrderedDict(d2)
        d2.update(stats)
        del d2['ProtocolDir']

        mapper = self.getElement('Color Mapper')
        mapper.setArgList(d2.keys())

        return mapper.getColor(d2)

    def colorMapChanged(self):
        self.colorsValid = False
        self.update()

    def update(self):
        if not self.analysisValid:
            print "Updating analysis.."
            map = self.currentMap
            if map is None:
                return
            scans = map.scans

            ## Get a list of all stimulations in the map and their times.
            sites = []
            for s in scans:
                sites.extend(s.getTimes())
            sites.sort(key=lambda i: i[1])

            ## get list of all events
            events = []
            for scan in scans:
                ev = scan.getAllEvents()
                if ev is not None:
                    events.append(ev.copy())

            ## set up table of per-stimulation data
            spontRates = np.zeros(len(sites),
                                  dtype=[('ProtocolDir', object),
                                         ('start', float), ('stop', float),
                                         ('spontRate', float),
                                         ('filteredSpontRate', float)])
            spontRates[:] = [s + (0, 0) for s in sites]  ## fill with data

            filtered = None
            if len(events) > 0:
                events = np.concatenate(events)
                filtered = self.filterStage.process(events)

                ## compute spontaneous rates
                sr = self.spontRateStage.process(spontRates, filtered)
                spontRates['spontRate'] = sr['spontRate']
                spontRates['filteredSpontRate'] = sr['filteredSpontRate']
            else:
                sr = {'ampMean': 0, 'ampStdev': 0}

            output = self.statsStage.process(map, spontRates, filtered,
                                             sr['ampMean'], sr['ampStdev'])
            self.analysisValid = True

        if not self.colorsValid:
            self.currentMap.recolor()
            self.colorsValid = True

    def invalidate(self):
        print "invalidate."
        self.analysisValid = False
        self.colorsValid = False

    def recalcClicked(self):
        self.invalidate()
        self.update()

    def updateTimes(self):
        self.params['Spontaneous Rate',
                    'Stop Time'] = self.params['Time Ranges', 'Direct Start']
        self.params['Analysis Methods',
                    'Stimulus Time'] = self.params['Time Ranges', 'Stimulus']
        self.params['Analysis Methods',
                    'Pre Stop'] = self.params['Time Ranges', 'Direct Start']
        self.params['Analysis Methods',
                    'Post Start'] = self.params['Time Ranges', 'Post Start']
        self.params['Analysis Methods',
                    'Post Stop'] = self.params['Time Ranges', 'Post Stop']

    def scanPointClicked(self, gitem, points):
        plot = self.getElement('Data Plot', create=True)
        plot.clear()
        scan = gitem.scan
        scan.displayData(points[0].data(), plot, 'w')

    def mapPointClicked(self, gitem, points):
        plot = self.getElement('Data Plot', create=True)
        plot.clear()
        data = []
        for p in points:
            for source in p.data()['sites']:
                data.append(source)
            #data.extend(p.data)
        for i in range(len(data)):
            scan, fh = data[i]
            scan.displayData(fh,
                             plot,
                             pen=(i, len(data) * 1.3),
                             eventFilter=self.filterStage.process)

        self.getElement('Stats Table').setData(points[0].data())

    def storeToDB(self):
        try:
            self.update()

            ## Determine currently selected table to store to
            dbui = self.getElement('Map Loader').dbGui
            identity = self.dbIdentity + '.sites'
            mapTable = dbui.getTableName('Photostim.maps')
            table = dbui.getTableName(identity)
            db = dbui.getDb()

            if db is None:
                raise Exception("No DB selected")

            fields = OrderedDict([
                ('Map', {
                    'Type': 'int',
                    'Link': mapTable
                }),
                #('CellDir', 'directory:Cell'),
                ('FirstSite', 'directory:Protocol'),
                ('Sites', 'blob'),
                ('PoissonScore', 'real'),
                ('PoissonScore_Pre', 'real'),
                ('PoissonAmpScore', 'real'),
                ('PoissonAmpScore_Pre', 'real'),
                ('HasInput', 'int'),
                ('FirstLatency', 'real'),
                ('ZScore', 'real'),
                ('FitAmpSum', 'real'),
                ('FitAmpSum_Pre', 'real'),
                ('NumEvents', 'real'),
                ('SpontRate', 'real'),
                ('DirectPeak', 'real'),
                ('Region', 'text'),
            ])

            mapRec = self.currentMap.getRecord()
            data = []
            for spot in self.currentMap.spots:
                rec = {}
                for k in fields:
                    if k in spot['data']:
                        rec[k] = spot['data'][k]
                #rec['CellDir'] = mapRec['cell']
                rec['Map'] = self.currentMap.rowID
                sites = [s[1] for s in spot['data']['sites']]
                rec['FirstSite'] = sites[0]
                rec['Sites'] = [db.getDirRowID(s) for s in sites]
                data.append(rec)

            with db.transaction():
                ## Make sure target table exists and has correct columns, links to input file
                db.checkTable(table,
                              owner=identity,
                              columns=fields,
                              create=True,
                              addUnknownColumns=True,
                              indexes=[['Map']])

                # delete old
                db.delete(table, where={'Map': self.currentMap.rowID})

                # write new
                with pg.ProgressDialog("Storing map data...", 0, 100) as dlg:
                    for n, nmax in db.iterInsert(table, data, chunkSize=100):
                        dlg.setMaximum(nmax)
                        dlg.setValue(n)
                        if dlg.wasCanceled():
                            raise HelpfulException(
                                "Scan store canceled by user.",
                                msgType='status')
            self.storeBtn.success()
        except:
            self.storeBtn.failure()
            raise

    def processRegions(self):
        ## Compute regions for each spot
        for spot in self.currentMap.spots:
            dh = spot['data']['sites'][0][1]
            pos = spot['pos']
            rgn = self.regions.getRegion(pos)
            spot['data']['Region'] = rgn
            #print dh,rgn

        ## Store ROI positions with cell
        cell = self.currentMap.getRecord()['cell']
        rgns = self.regions.getRegions()
        cell.setInfo(MapAnalyzer_Regions=rgns)

    def loadFromDB(self):
        ## read in analysis from DB

        dbui = self.getElement('Map Loader').dbGui
        identity = self.dbIdentity + '.sites'
        mapTable = dbui.getTableName('Photostim.maps')
        table = dbui.getTableName(identity)
        db = dbui.getDb()

        if db is None:
            raise Exception("No DB selected")
        if not db.hasTable(table):
            return None

        fields = OrderedDict([
            ('Map', {
                'Type': 'int',
                'Link': mapTable
            }),
            #('Sites', 'blob'),
            ('PoissonScore', 'real'),
            ('PoissonScore_Pre', 'real'),
            ('PoissonAmpScore', 'real'),
            ('PoissonAmpScore_Pre', 'real'),
            ('HasInput', 'int'),
            ('FirstLatency', 'real'),
            ('ZScore', 'real'),
            ('FitAmpSum', 'real'),
            ('FitAmpSum_Pre', 'real'),
            ('NumEvents', 'real'),
            ('SpontRate', 'real'),
            ('DirectPeak', 'real'),
            ('Region', 'text'),
        ])

        #mapRec = self.currentMap.getRecord()
        recs = db.select(table, ['rowid', '*'],
                         where={'Map': self.currentMap.rowID})
        if len(recs) == len(self.currentMap.spots):
            for i, spot in enumerate(self.currentMap.spots):

                for k in fields.keys() + recs[i].keys():
                    spot['data'][k] = recs[i].get(k, None)
            self.analysisValid = True
            print "reloaded analysis from DB", self.currentMap.rowID
        else:
            print "analysis incomplete:", len(recs), len(self.currentMap.spots)