def enumerateDevices(cls): """Generate a list of all Scientifica devices found in the system. Sets Scientifica.availableDevices to a dict of {name: port} pairs. This works by searching for USB serial devices with device IDs used by Scientifica (vid=0403, pid=6010) and sending a single serial request. """ import serial.tools.list_ports coms = serial.tools.list_ports.comports() devs = {} for com, name, ident in coms: # several different ways this can appear: # VID_0403+PID_6010 # VID_0403&PID_6010 # VID:PID=0403:6010 if ('VID_0403' not in ident or 'PID_6010' not in ident) and '0403:6010' not in ident: continue com = cls.normalizePortName(com) if com in cls.openDevices: name = cls.openDevices[com].getDescription() devs[name] = com else: try: s = Scientifica(port=com, ctrl_version=None) devs[s.getDescription()] = com s.close() except Exception: printExc("Error while initializing Scientifica device at %s (the device at this port will not be available):" % com) cls.availableDevices = devs
def quit(self): if self.hasQuit: return try: self.recordThread.sigShowMessage.disconnect(self.showMessage) self.recordThread.finished.disconnect(self.recordThreadStopped) self.recordThread.sigRecordingFailed.disconnect(self.recordingFailed) self.recordThread.sigRecordingFinished.disconnect(self.recordFinished) except TypeError: pass try: self.cam.sigNewFrame.disconnect(self.newFrame) self.cam.sigCameraStopped.disconnect(self.cameraStopped) self.cam.sigCameraStarted.disconnect(self.cameraStarted) self.cam.sigShowMessage.disconnect(self.showMessage) except TypeError: pass self.hasQuit = True if self.cam.isRunning(): self.cam.stop() if not self.cam.wait(10000): printExc("Timed out while waiting for acq thread exit!") if self.recordThread.isRunning(): self.recordThread.stop() if not self.recordThread.wait(10000): raise Exception("Timed out while waiting for rec. thread exit!") del self.recordThread ## Required due to cyclic reference
def run(self): ### run is called indirectly (by C somewhere) by calling start() from the parent thread; DO NOT call directly self.stopThread = False while True: try: with self.camLock: handleCamFrames = self.newCamFrames[:] self.newCamFrames = [] except: debug.printExc('Error in camera recording thread:') break try: self.handleCamFrames(handleCamFrames) #while len(handleCamFrames) > 0: #self.handleCamFrame(handleCamFrames.pop(0)) except: debug.printExc('Error in camera recording thread:') self.toggleRecord(False) #self.emit(QtCore.SIGNAL('recordingFailed')) self.sigRecordingFailed.emit() time.sleep(10e-3) #print " RecordThread run: stop check" with self.lock as l: #print " RecordThread run: got lock" if self.stopThread: #print " RecordThread run: stop requested, exiting loop" break
def run(self): try: with self.lock: videoRequested = self._video closeShutter = self._closeShutter if (videoRequested or not closeShutter) and self.laserDev is not None and self.laserDev.hasShutter: # force shutter to stay open for the duration of the acquisition self.laserDev.openShutter() while True: # take one frame self.acquireFrame(allowBlanking=False) # See whether acquisition should end with self.lock: video, abort = self._video, self._abort if video is False: break if abort is True: raise Exception("Imaging acquisition aborted") except Exception: self.sigAborted.emit() printExc("Error in imaging acquisition thread.") finally: if videoRequested: self.sigVideoStopped.emit() if closeShutter and self.laserDev is not None and self.laserDev.hasShutter: self.laserDev.closeShutter()
def run(self): self.stopThread = False while self.stopThread is False: try: pos = self.dev.getPosition() time.sleep(self.interval) except: debug.printExc("Error in Filter Wheel poll thread:") time.sleep(1.0)
def setCurrentLog(self, dh): if dh is not None: try: self.loadFile(dh['log.txt'].name()) self.ui.dirLabel.setText("Currently displaying " + self.currentLogDir.name(relativeTo=self.manager.baseDir)+'/log.txt') except: debug.printExc("Error loading log file:") self.clear() self.ui.dirLabel.setText("") else: self.clear() self.ui.dirLabel.setText("")
def readPosition(self): try: with self.driverLock: pos = np.array(self.mp285.getPos()) return pos*self.scale except TimeoutError: self.sigError.emit("Read timeout--press reset button on MP285.") return None except: self.sigError.emit("Read error--see console.") debug.printExc("Error getting packet:") return None
def readPosition(self): try: with self.driverLock: pos = np.array(self.mp285.getPos()) return pos * self.scale except TimeoutError: self.sigError.emit("Read timeout--press reset button on MP285.") return None except: self.sigError.emit("Read error--see console.") debug.printExc("Error getting packet:") return None
def makeError1(self): ### just for testing error logging try: self.makeError2() # print x except: t, exc, tb = sys.exc_info() # logExc(message="This button doesn't work", reasons='reason a, reason b', docs='documentation') # if isinstance(exc, HelpfulException): # exc.prependErr("Button doesn't work", (t,exc,tb), reasons = ["It's supposed to raise an error for testing purposes", "You're doing it wrong."]) # raise # else: printExc("This is the message sent to printExc.")
def makeError1(self): ### just for testing error logging try: self.makeError2() #print x except: t, exc, tb = sys.exc_info() #logExc(message="This button doesn't work", reasons='reason a, reason b', docs='documentation') #if isinstance(exc, HelpfulException): #exc.prependErr("Button doesn't work", (t,exc,tb), reasons = ["It's supposed to raise an error for testing purposes", "You're doing it wrong."]) #raise #else: printExc("This is the message sent to printExc.")
def startAcquireClicked(self, mode): """User clicked the acquire video button. """ try: self.cam.setParam('triggerMode', 'Normal', autoRestart=False) self.setBinning(autoRestart=False) self.setExposure(autoRestart=False) self.updateRegion(autoRestart=False) self.cam.start() Manager.logMsg("Camera started aquisition.", importance=0) except: self.imagingCtrl.acquisitionStopped() printExc("Error starting camera:")
def setCurrentLog(self, dh): if dh is not None: try: self.loadFile(dh['log.txt'].name()) self.ui.dirLabel.setText("Currently displaying " + self.currentLogDir.name( relativeTo=self.manager.baseDir) + '/log.txt') except: debug.printExc("Error loading log file:") self.clear() self.ui.dirLabel.setText("") else: self.clear() self.ui.dirLabel.setText("")
def listFileTypes(): """Return a list of the names of all available fileType subclasses.""" global KNOWN_FILE_TYPES if KNOWN_FILE_TYPES is None: KNOWN_FILE_TYPES = {} files = os.listdir(os.path.dirname(__file__)) for f in ['filetypes.py', '__init__.py', 'FileType.py']: if f in files: files.remove(f) typs = [os.path.splitext(f)[0] for f in files if f[-3:] == '.py'] for typ in typs: try: getFileType(typ) except: debug.printExc("Error loading file type library '%s':" % typ) return KNOWN_FILE_TYPES.keys()
def loadFileRequested(self, fhList): canvas = self.getElement('Canvas') model = self.dataModel 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) else: #self.loadScan(fh) debug.printExc("MapAnalyzer does not yet support loading scans") return False return True except: debug.printExc("Error loading file %s" % fh.name()) return False
def toggleAcquire(self): if self.ui.acquireVideoBtn.isChecked(): try: self.cam.setParam('triggerMode', 'Normal', autoRestart=False) self.setBinning(autoRestart=False) self.setExposure(autoRestart=False) self.updateRegion(autoRestart=False) self.cam.start() Manager.logMsg("Camera started aquisition.", importance=0) except: self.ui.acquireVideoBtn.setChecked(False) printExc("Error starting camera:") else: #print "ACQ untoggled, stop record" self.toggleRecord(False) self.cam.stop() Manager.logMsg("Camera stopped acquisition.", importance=0)
def run(self): # run is invoked in the worker thread automatically after calling start() self.stopThread = False while True: with self.lock: if self.stopThread: break newFrames = self.newFrames[:] self.newFrames = [] try: self.handleFrames(newFrames) except: debug.printExc('Error in image recording thread:') self.sigRecordingFailed.emit() time.sleep(100e-3)
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) else: self.loadScan(fh) return True except: debug.printExc("Error loading file %s" % fh.name()) return False dlg += 1 if dlg.wasCanceled(): return
def quit(self): self.imagingCtrl.quit() if self.hasQuit: return try: self.cam.sigNewFrame.disconnect(self.newFrame) self.cam.sigCameraStopped.disconnect(self.cameraStopped) self.cam.sigCameraStarted.disconnect(self.cameraStarted) self.cam.sigShowMessage.disconnect(self.showMessage) except TypeError: pass self.hasQuit = True if self.cam.isRunning(): self.cam.stop() if not self.cam.wait(10000): printExc("Timed out while waiting for acq thread exit!")
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) else: self.loadScan(fh) return True except: debug.printExc("Error loading file %s" % fh.name()) return False dlg += 1 if dlg.wasCancelled(): return
def __init__(self, host, flowchartDir=None, dbIdentity="EventDetector", dbCtrl=None): AnalysisModule.__init__(self, host) if flowchartDir is None: flowchartDir = os.path.join(os.path.abspath(os.path.split(__file__)[0]), "flowcharts") self.flowchart = Flowchart(filePath=flowchartDir) self.dbIdentity = dbIdentity ## how we identify to the database; this determines which tables we own #self.loader = FileLoader.FileLoader(host.dataManager()) #self.setCentralWidget(self.flowchart.widget()) #self.ui.chartDock1.setWidget(self.flowchart.widget()) self.flowchart.addInput("dataIn") self.flowchart.addOutput('events') #self.flowchart.addOutput('regions', multi=True) self.flowchart.sigChartLoaded.connect(self.connectPlots) if dbCtrl == None: self.dbCtrl = DBCtrl(self, identity=self.dbIdentity) self.dbCtrl.storeBtn.clicked.connect(self.storeClicked) else: self.dbCtrl = dbCtrl(self, identity=self.dbIdentity) #self.ctrl = QtGui.QLabel('LABEL') self.ctrl = self.flowchart.widget() self._elements_ = OrderedDict([ ('File Loader', {'type': 'fileInput', 'size': (200, 300), 'host': self}), ('Database', {'type': 'ctrl', 'object': self.dbCtrl, 'size': (200,300), 'pos': ('bottom', 'File Loader')}), ('Data Plot', {'type': 'plot', 'pos': ('right',), 'size': (800, 300)}), ('Detection Opts', {'type': 'ctrl', 'object': self.ctrl, 'pos': ('bottom', 'Database'), 'size': (200, 500)}), ('Filter Plot', {'type': 'plot', 'pos': ('bottom', 'Data Plot'), 'size': (800, 300)}), ('Output Table', {'type': 'table', 'pos': ('bottom', 'Filter Plot'), 'optional': True, 'size': (800,200)}), ]) self.initializeElements() try: ## load default chart self.flowchart.loadFile(os.path.join(flowchartDir, 'default.fc')) except: debug.printExc('Error loading default flowchart:') self.flowchart.sigOutputChanged.connect(self.outputChanged)
def run(self): self.stopThread = False with self.driverLock: self.fwPosChanged.emit(self.driver.getPos()) while True: try: with self.driverLock: pos = self.driver.getPos() self.fwPosChanged.emit(pos) time.sleep(0.5) except: debug.printExc("Error in Filter Wheel communication thread:") self.lock.lock() if self.stopThread: self.lock.unlock() break self.lock.unlock() time.sleep(0.02) self.driver.close()
def __init__(self, dm, config, name): # Generate config to use for DAQ self.devLock = Mutex(Mutex.Recursive) daqConfig = { 'command': config['Command'], 'primary': config['ScaledSignal'], } self.holding = { 'vc': config.get('vcHolding', -0.05), 'ic': config.get('icHolding', 0.0) } self.mode = 'i=0' self.config = config DAQGeneric.__init__(self, dm, daqConfig, name) try: self.setHolding() except: printExc("Error while setting holding value:") # Start a remote process to run the simulation. self.process = mp.Process() rsys = self.process._import('sys') rsys._setProxyOptions( returnType='proxy' ) # need to access remote path by proxy, not by value rsys.path.append(os.path.abspath(os.path.dirname(__file__))) if config['simulator'] == 'builtin': self.simulator = self.process._import('hhSim') elif config['simulator'] == 'neuron': self.simulator = self.process._import('neuronSim') dm.declareInterface(name, ['clamp'], self)
def __init__(self, dm, config, name): # Generate config to use for DAQ daqConfig = {} self.devLock = Mutex(Mutex.Recursive) daqConfig = { 'command': config['Command'], 'primary': config['ScaledSignal'], } self.holding = { 'vc': config.get('vcHolding', -0.05), 'ic': config.get('icHolding', 0.0) } self.mode = 'i=0' self.config = config DAQGeneric.__init__(self, dm, daqConfig, name) try: self.setHolding() except: printExc("Error while setting holding value:") # Start a remote process to run the simulation. self.process = mp.Process() rsys = self.process._import('sys') rsys._setProxyOptions(returnType='proxy') # need to access remote path by proxy, not by value rsys.path.append(os.path.abspath(os.path.dirname(__file__))) if config['simulator'] == 'builtin': self.simulator = self.process._import('hhSim') elif config['simulator'] == 'neuron': self.simulator = self.process._import('neuronSima') dm.declareInterface(name, ['clamp'], self)
def suggestWriteType(data, fileName=None): """Guess which fileType class should be used to write data. If fileName is specified, this may influence the guess. Return the name of the class.""" maxVal = None maxType = None #print "Suggest for type %s, name %s" % (type(data), str(fileName)) for typ in listFileTypes(): try: cls = getFileType(typ) except: debug.printExc("ignoring filetype %s" % typ) continue priority = cls.acceptsData(data, fileName) #print "filetype %s has priority %d" %(typ, int(priority)) if priority is False: continue else: if maxVal is None or priority > maxVal: maxVal = priority maxType = typ #print "Suggesting", maxType return maxType
def run(self): self.stopThread = False with self.driverLock: self.sigWavelengthChanged.emit(self.driver.getWavelength() * 1e-9) while True: try: with self.driverLock: power = self.driver.getPower() * 1e-3 wl = self.driver.getWavelength() * 1e-9 self.sigPowerChanged.emit(power) self.sigWavelengthChanged.emit(wl) time.sleep(0.5) except: debug.printExc("Error in Coherent laser communication thread:") self.lock.lock() if self.stopThread: self.lock.unlock() break self.lock.unlock() time.sleep(0.02) self.driver.close()
def __init__(self, host, flowchartDir=None): AnalysisModule.__init__(self, host) self.dbIdentity = "IVCurveAnalyzer" ## how we identify to the database; this determines which tables we own if flowchartDir is None: flowchartDir = os.path.join(os.path.abspath(os.path.split(__file__)[0]), "flowcharts") self.flowchart = Flowchart(filePath=flowchartDir) self.flowchart.addInput("dataIn") #self.flowchart.addOutput('events') self.flowchart.addOutput('regions', multi=True) #self.flowchart.sigChartLoaded.connect(self.connectPlots) ### DBCtrl class is from EventDetector -- need to make my own here #self.dbCtrl = DBCtrl(self, identity=self.dbIdentity) #self.dbCtrl.storeBtn.clicked.connect(self.storeClicked) self.ctrl = self.flowchart.widget() self._elements_ = OrderedDict([ ('File Loader', {'type': 'fileInput', 'size': (200, 300), 'host': self}), #('Database', {'type': 'ctrl', 'object': self.dbCtrl, 'size': (200,300), 'pos': ('bottom', 'File Loader')}), ('Data Plot', {'type': 'plot', 'pos': ('right',), 'size': (800, 300)}), ('Detection Opts', {'type': 'ctrl', 'object': self.ctrl, 'pos': ('bottom', 'File Loader'), 'size': (200, 500)}), ('IV Plot', {'type': 'plot', 'pos': ('bottom', 'Data Plot'), 'size': (400, 300)}), ('Output Table', {'type': 'table', 'pos': ('bottom', 'IV Plot'), 'optional': True, 'size': (800,200)}), ('FI Plot', {'type': 'plot', 'pos': ('right', 'IV Plot'), 'size': (400, 300)}), ]) self.initializeElements() try: ## load default chart self.flowchart.loadFile(os.path.join(flowchartDir, 'default.fc')) except: debug.printExc('Error loading default flowchart:')
def run(self): self.stopThread = False with self.driverLock: self.sigWavelengthChanged.emit(self.driver.getWavelength()*1e-9) while True: try: with self.driverLock: power = self.driver.getPower() * 1e-3 wl = self.driver.getWavelength()*1e-9 self.sigPowerChanged.emit(power) self.sigWavelengthChanged.emit(wl) time.sleep(0.5) except: debug.printExc("Error in Coherent laser communication thread:") self.lock.lock() if self.stopThread: self.lock.unlock() break self.lock.unlock() time.sleep(0.02) self.driver.close()
def __init__(self, dm, config, name): # Generate config to use for DAQ daqConfig = {} for ch in ['GainChannel', 'LPFChannel', 'ModeChannel']: if ch not in config: continue daqConfig[ch] = config[ch].copy() #if 'GainChannel' in config: # daqConfig['gain'] = {'type': 'ai', 'channel': config['GainChannel']} #if 'LPFChannel' in config: # daqConfig['LPF'] = {'type': 'ai', 'channel': config['LPFChannel'], 'units': 'Hz'} if 'ScaledSignal' in config: #daqConfig['primary'] = {'type': 'ai', 'channel': config['ScaledSignal']} daqConfig['primary'] = config['ScaledSignal'] if config['ScaledSignal'].get('type', None) != 'ai': raise Exception("AxoPatch200: ScaledSignal configuration must have type:'ai'") if 'Command' in config: #daqConfig['command'] = {'type': 'ao', 'channel': config['Command']} daqConfig['command'] = config['Command'] if config['Command'].get('type', None) != 'ao': raise Exception("AxoPatch200: ScaledSignal configuration must have type:'ao'") ## Note that both of these channels can be present, but we will only ever record from one at a time. ## Usually, we'll record from "I OUTPUT" in current clamp and "10 Vm OUTPUT" in voltage clamp. if 'SecondaryVCSignal' in config: self.hasSecondaryChannel = True #daqConfig['secondary'] = {'type': 'ai', 'channel': config['SecondaryVCSignal']} daqConfig['secondary'] = config['SecondaryVCSignal'] if config['SecondaryVCSignal'].get('type', None) != 'ai': raise Exception("AxoPatch200: SecondaryVCSignal configuration must have type:'ai'") elif 'SecondaryICSignal' in config: self.hasSecondaryChannel = True #daqConfig['secondary'] = {'type': 'ai', 'channel': config['SecondaryICSignal']} daqConfig['secondary'] = config['SecondaryICSignal'] if config['SecondaryICSignal'].get('type', None) != 'ai': raise Exception("AxoPatch200: SecondaryICSignal configuration must have type:'ai'") else: self.hasSecondaryChannel = False self.version = config.get('version', '200B') # Axopatch gain telegraph # telegraph should not read below 2 V in CC mode self.gain_tel = np.array([0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5]) self.gain_vm = np.array([0.5, 0.5, 0.5, 0.5, 1, 2, 5, 10, 20, 50, 100, 200, 500]) * 1e9 ## values in mv/pA self.gain_im = np.array([0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, 100, 200, 500]) ## values in mV/mV # Axopatch Lowpass Bessel Filter self.lpf_tel = np.array([2.0, 4.0, 6.0, 8.0, 10.0]) self.lpf_freq = np.array([1.0, 2.0, 5.0, 10.0, 50.0]) if self.version == '200': # telegraph voltage/output translation from the Axopatch 200 amplifier self.mode_tel = np.array([6, 4, 2]) self.modeNames = OrderedDict([(0, 'V-Clamp'), (1, 'Track'), (2, 'I-Clamp')]) self.ivModes = {'V-Clamp':'vc', 'Track':'ic', 'I-Clamp':'ic', 'vc':'vc', 'ic':'ic'} self.modeAliases = {'ic': 'I-Clamp', 'i=0': 'Track', 'vc': 'V-Clamp'} elif self.version == '200A': # telegraph voltage/output translation from the Axopatch 200 amplifier self.mode_tel = np.array([6, 4, 2, 1]) self.modeNames = OrderedDict([(0, 'V-Clamp'), (1, 'Track'), (2, 'I-Clamp Normal'), (3, 'I-Clamp Fast'), ]) self.ivModes = {'V-Clamp':'vc', 'Track':'vc', 'I-Clamp Fast':'ic', 'I-Clamp Normal':'ic', 'vc':'vc', 'ic':'ic'} self.modeAliases = {'ic': 'I-Clamp Fast', 'i=0': 'Track', 'vc': 'V-Clamp'} elif self.version == '200B': # telegraph voltage/output translation from the Axopatch 200 amplifier self.mode_tel = np.array([6, 4, 3, 2, 1]) self.modeNames = OrderedDict([(0, 'V-Clamp'), (2, 'I=0'), (4, 'I-Clamp Fast'), (3, 'I-Clamp Normal'), (1, 'Track'), ]) self.ivModes = {'V-Clamp':'vc', 'Track':'vc', 'I=0':'ic', 'I-Clamp Fast':'ic', 'I-Clamp Normal':'ic', 'vc':'vc', 'ic':'ic'} self.modeAliases = {'ic': 'I-Clamp Fast', 'i=0': 'I=0', 'vc': 'V-Clamp'} self.lpf_freq[-1] = 100.0 # 200B's highest LPF value is 100kHz instead of 50. else: raise Exception("AxoPatch200: version must be '200', '200A' or '200B' (got %r)" % version) self.holding = { 'vc': config.get('vcHolding', -0.05), 'ic': config.get('icHolding', 0.0) } self.config = config self.modeLock = Mutex(Mutex.Recursive) ## protects self.mdCanceled self.devLock = Mutex(Mutex.Recursive) ## protects self.holding, possibly self.config, ..others, perhaps? self.mdCanceled = False DAQGeneric.__init__(self, dm, daqConfig, name) self.modeDialog = QtGui.QMessageBox() self.modeDialog.hide() self.modeDialog.setModal(False) self.modeDialog.setWindowTitle("Mode Switch Request") self.modeDialog.addButton(self.modeDialog.Cancel) self.modeDialog.buttonClicked.connect(self.modeDialogClicked) self.sigShowModeDialog.connect(self.showModeDialog) self.sigHideModeDialog.connect(self.hideModeDialog) try: self.setHolding() except: printExc("Error while setting holding value:") dm.declareInterface(name, ['clamp'], self)
def drawFrame(self): if self.hasQuit: return #sys.stdout.write('+') try: ## If we last drew a frame < 1/30s ago, return. t = pg.ptime.time() if (self.lastDrawTime is not None) and (t - self.lastDrawTime < .03): #sys.stdout.write('-') return ## if there is no new frame and no controls have changed, just exit if not self._updateFrame and self.nextFrame is None: #sys.stdout.write('-') return self._updateFrame = False ## If there are no new frames and no previous frames, then there is nothing to draw. if self.currentFrame is None and self.nextFrame is None: #sys.stdout.write('-') return prof = pg.debug.Profiler() ## We will now draw a new frame (even if the frame is unchanged) if self.lastDrawTime is not None: fps = 1.0 / (t - self.lastDrawTime) self.displayFps = fps self.lastDrawTime = t prof() ## Handle the next available frame, if there is one. if self.nextFrame is not None: self.currentFrame = self.nextFrame self.nextFrame = None data = self.currentFrame.getImage() info = self.currentFrame.info() prof() ## divide the background out of the current frame if needed data = self.bgCtrl.processImage(data) prof() ## Set new levels if auto gain is enabled self.contrastCtrl.processImage(data) prof() ## update image in viewport self._imageItem.updateImage( data.copy()) # using data.copy() here avoids crashes! prof() self.imageUpdated.emit(self.currentFrame) prof() prof.finish() except: printExc('Error while drawing new frames:') finally: pass
def __init__(self, host, flowchartDir=None, dbIdentity="EventDetector", dbCtrl=None): AnalysisModule.__init__(self, host) if flowchartDir is None: flowchartDir = os.path.join( os.path.abspath(os.path.split(__file__)[0]), "flowcharts") self.flowchart = Flowchart(filePath=flowchartDir) self.dbIdentity = dbIdentity ## how we identify to the database; this determines which tables we own #self.loader = FileLoader.FileLoader(host.dataManager()) #self.setCentralWidget(self.flowchart.widget()) #self.ui.chartDock1.setWidget(self.flowchart.widget()) self.flowchart.addInput("dataIn") self.flowchart.addOutput('events') #self.flowchart.addOutput('regions', multi=True) self.flowchart.sigChartLoaded.connect(self.connectPlots) if dbCtrl == None: self.dbCtrl = DBCtrl(self, identity=self.dbIdentity) self.dbCtrl.storeBtn.clicked.connect(self.storeClicked) else: self.dbCtrl = dbCtrl(self, identity=self.dbIdentity) #self.ctrl = QtGui.QLabel('LABEL') self.ctrl = self.flowchart.widget() self._elements_ = OrderedDict([ ('File Loader', { 'type': 'fileInput', 'size': (200, 300), 'host': self }), ('Database', { 'type': 'ctrl', 'object': self.dbCtrl, 'size': (200, 300), 'pos': ('bottom', 'File Loader') }), ('Data Plot', { 'type': 'plot', 'pos': ('right', ), 'size': (800, 300) }), ('Detection Opts', { 'type': 'ctrl', 'object': self.ctrl, 'pos': ('bottom', 'Database'), 'size': (200, 500) }), ('Filter Plot', { 'type': 'plot', 'pos': ('bottom', 'Data Plot'), 'size': (800, 300) }), ('Output Table', { 'type': 'table', 'pos': ('bottom', 'Filter Plot'), 'optional': True, 'size': (800, 200) }), ]) self.initializeElements() try: ## load default chart self.flowchart.loadFile(os.path.join(flowchartDir, 'default.fc')) except: debug.printExc('Error loading default flowchart:') self.flowchart.sigOutputChanged.connect(self.outputChanged)
def run(self): self.stopThread = False #self.sp = serial.Serial(int(self.port), baudrate=self.baud, bytesize=serial.EIGHTBITS) #time.sleep(3) ## Wait a few seconds for the mouse to say hello ## clear buffer before starting #if self.sp.inWaiting() > 0: #print "Discarding %d bytes" % self.sp.inWaiting() #self.sp.read(self.sp.inWaiting()) #import wingdbstub print " Starting MP285 thread: 0x%x" % int( QtCore.QThread.currentThreadId()) #import sip #print " also known as 0x%x" % sip.unwrapinstance(self) velocity = np.array([0, 0, 0]) pos = [0, 0, 0] try: self.getImmediatePos() monitor = True except: debug.printExc("Sutter MP285: Cannot determine position:") monitor = False while True: try: ## Lock and copy state to local variables with self.lock: update = self.update self.update = False limits = deepcopy(self.limits) maxSpeed = self.maxSpeed newVelocity = np.array(self.velocity[:]) resolution = self.resolution limitChanged = self.limitChanged self.limitChanged = False ## if limits have changed, inform the device if monitor and limitChanged: ## monitor is only true if this is a customized device with limit checking self.sendLimits() ## If requested velocity is different from the current velocity, handle that. if np.any(newVelocity != velocity): speed = np.clip(np.sum(newVelocity**2)**0.5, 0., 1.) ## should always be 0.0-1.0 #print "new velocity:", newVelocity, "speed:", speed if speed == 0: nv = np.array([0, 0, 0]) else: nv = newVelocity / speed speed = np.clip(speed, 0, maxSpeed) #print "final speed:", speed ## stop current move, get position, start new move #print "stop.." self.stopMove() #print "stop done." #print "getpos..." pos1 = self.readPosition() if pos1 is not None: if speed > 0: #print " set new velocity" self.writeVelocity(speed, nv, limits=limits, pos=pos1, resolution=resolution) #print " done" ## report current position velocity = newVelocity #print "done" ## If velocity is 0, we can do some position checks if np.all(velocity == 0): newPos = None if update: newPos = self.readPosition() elif monitor: newPos = self.getImmediatePos() if newPos is not None: ## If position has changed, emit a signal. change = [ newPos[i] - pos[i] for i in range(len(newPos)) ] pos = newPos if any(change): #self.emit(QtCore.SIGNAL('positionChanged'), {'rel': change, 'abs': self.pos}) self.sigPositionChanged.emit({ 'rel': change, 'abs': pos }) else: ## moving; make a guess about the current position pass except: pass debug.printExc("Error in MP285 thread:") self.lock.lock() if self.stopThread: self.lock.unlock() break self.lock.unlock() time.sleep(0.02) self.mp285.close()
def __init__(self, host, flowchartDir=None): AnalysisModule.__init__(self, host) self.dbIdentity = "IVCurveAnalyzer" ## how we identify to the database; this determines which tables we own if flowchartDir is None: flowchartDir = os.path.join( os.path.abspath(os.path.split(__file__)[0]), "flowcharts") self.flowchart = Flowchart(filePath=flowchartDir) self.flowchart.addInput("dataIn") #self.flowchart.addOutput('events') self.flowchart.addOutput('regions', multi=True) #self.flowchart.sigChartLoaded.connect(self.connectPlots) ### DBCtrl class is from EventDetector -- need to make my own here #self.dbCtrl = DBCtrl(self, identity=self.dbIdentity) #self.dbCtrl.storeBtn.clicked.connect(self.storeClicked) self.ctrl = self.flowchart.widget() self._elements_ = OrderedDict([ ('File Loader', { 'type': 'fileInput', 'size': (200, 300), 'host': self }), #('Database', {'type': 'ctrl', 'object': self.dbCtrl, 'size': (200,300), 'pos': ('bottom', 'File Loader')}), ('Data Plot', { 'type': 'plot', 'pos': ('right', ), 'size': (800, 300) }), ('Detection Opts', { 'type': 'ctrl', 'object': self.ctrl, 'pos': ('bottom', 'File Loader'), 'size': (200, 500) }), ('IV Plot', { 'type': 'plot', 'pos': ('bottom', 'Data Plot'), 'size': (400, 300) }), ('Output Table', { 'type': 'table', 'pos': ('bottom', 'IV Plot'), 'optional': True, 'size': (800, 200) }), ('FI Plot', { 'type': 'plot', 'pos': ('right', 'IV Plot'), 'size': (400, 300) }), ]) self.initializeElements() try: ## load default chart self.flowchart.loadFile(os.path.join(flowchartDir, 'default.fc')) except: debug.printExc('Error loading default flowchart:')
def __init__(self, image=None, **opts): ## If no image was specified, check for a file handle.. if image is None: image = opts.get('handle', None) item = None self.data = None if isinstance(image, QtGui.QGraphicsItem): item = image elif isinstance(image, np.ndarray): self.data = image elif isinstance(image, DataManager.FileHandle): opts['handle'] = image self.handle = image self.data = self.handle.read() if 'name' not in opts: opts['name'] = self.handle.shortName() try: if 'transform' in self.handle.info(): tr = pg.SRTTransform3D(self.handle.info()['transform']) tr = pg.SRTTransform(tr) ## convert to 2D opts['pos'] = tr.getTranslation() opts['scale'] = tr.getScale() opts['angle'] = tr.getRotation() else: ## check for older info formats if 'imagePosition' in self.handle.info(): opts['scale'] = self.handle.info()['pixelSize'] opts['pos'] = self.handle.info()['imagePosition'] elif 'Downsample' in self.handle.info(): ### Needed to support an older format stored by 2p imager if 'pixelSize' in self.handle.info(): opts['scale'] = self.handle.info()['pixelSize'] if 'microscope' in self.handle.info(): m = self.handle.info()['microscope'] opts['pos'] = m['position'][0:2] else: info = self.data._info[-1] opts['pos'] = info.get('imagePosition', None) elif hasattr(self.data, '_info'): info = self.data._info[-1] opts['scale'] = info.get('pixelSize', None) opts['pos'] = info.get('imagePosition', None) else: opts['defaultUserTransform'] = {'scale': (1e-5, 1e-5)} opts['scalable'] = True except: debug.printExc( 'Error reading transformation for image file %s:' % image.name()) if item is None: item = pg.ImageItem() CanvasItem.__init__(self, item, **opts) self.splitter = QtGui.QSplitter() self.splitter.setOrientation(QtCore.Qt.Vertical) self.layout.addWidget(self.splitter, self.layout.rowCount(), 0, 1, 2) self.filterGroup = pg.GroupBox("Image Filter") fgl = QtGui.QGridLayout() fgl.setContentsMargins(3, 3, 3, 3) fgl.setSpacing(1) self.filterGroup.setLayout(fgl) self.filter = ImageFilterWidget() self.filter.sigStateChanged.connect(self.filterStateChanged) fgl.addWidget(self.filter) self.splitter.addWidget(self.filterGroup) self.histogram = pg.HistogramLUTWidget() self.histogram.setImageItem(self.graphicsItem()) # addWidget arguments: row, column, rowspan, colspan self.splitter.addWidget(self.histogram) self.imgModeCombo = QtGui.QComboBox() self.imgModeCombo.addItems( ['SourceOver', 'Overlay', 'Plus', 'Multiply']) self.layout.addWidget(self.imgModeCombo, self.layout.rowCount(), 0, 1, 1) self.imgModeCombo.currentIndexChanged.connect(self.imgModeChanged) self.autoBtn = QtGui.QPushButton("Auto") self.autoBtn.setCheckable(True) self.autoBtn.setChecked(True) self.layout.addWidget(self.autoBtn, self.layout.rowCount() - 1, 1, 1, 1) self.timeSlider = QtGui.QSlider(QtCore.Qt.Horizontal) self.layout.addWidget(self.timeSlider, self.layout.rowCount(), 0, 1, 2) self.timeSlider.valueChanged.connect(self.timeChanged) # ## controls that only appear if there is a time axis self.timeControls = [self.timeSlider] if self.data is not None: if isinstance(self.data, pg.metaarray.MetaArray): self.filter.setInput(self.data.asarray()) else: self.filter.setInput(self.data) self.updateImage() # Needed to ensure selection box wraps the image properly tr = self.saveTransform() self.resetUserTransform() self.restoreTransform(tr)
def recordThreadStopped(self): self.endStack() self.ui.recordStackBtn.setEnabled( False) ## Recording thread has stopped, can't record anymore. printExc("Recording thread died! See console for error message.")
def __init__(self, host): AnalysisModule.__init__(self, host) if self.dataModel is None: raise Exception("Photostim analysis module requires a data model, but none is loaded yet.") self.dbIdentity = "Photostim" ## how we identify to the database; this determines which tables we own self.selectedSpot = None ## setup analysis flowchart modPath = os.path.abspath(os.path.split(__file__)[0]) flowchartDir = os.path.join(modPath, "analysis_fc") self.flowchart = Flowchart(filePath=flowchartDir) self.flowchart.addInput('events') self.flowchart.addInput('regions') self.flowchart.addInput('fileHandle') self.flowchart.addOutput('dataOut') self.analysisCtrl = self.flowchart.widget() ## color mapper self.mapper = ColorMapper.ColorMapper(filePath=os.path.join(modPath, "colormaps")) self.mapCtrl = QtGui.QWidget() self.mapLayout = QtGui.QVBoxLayout() self.mapCtrl.setLayout(self.mapLayout) self.mapLayout.splitter = QtGui.QSplitter() self.mapLayout.splitter.setOrientation(QtCore.Qt.Vertical) self.mapLayout.splitter.setContentsMargins(0,0,0,0) self.mapLayout.addWidget(self.mapLayout.splitter) self.mapLayout.splitter.addWidget(self.analysisCtrl) #self.mapLayout.splitter.addWidget(QtGui.QSplitter()) self.mapLayout.splitter.addWidget(self.mapper) #self.mapLayout.splitter.addWidget(self.recolorBtn) self.recolorLayout = QtGui.QHBoxLayout() self.recolorWidget = QtGui.QWidget() self.mapLayout.splitter.addWidget(self.recolorWidget) self.recolorWidget.setLayout(self.recolorLayout) self.recolorBtn = QtGui.QPushButton('Recolor') self.recolorLayout.addWidget(self.recolorBtn) self.recolorParallelCheck = QtGui.QCheckBox('Parallel') self.recolorParallelCheck.setChecked(True) self.recolorLayout.addWidget(self.recolorParallelCheck) ## scatter plot self.scatterPlot = ScatterPlotter() self.scatterPlot.sigClicked.connect(self.scatterPlotClicked) ## setup map DB ctrl self.dbCtrl = DBCtrl(self, self.dbIdentity) ## storage for map data #self.scanItems = {} self.scans = [] #self.seriesScans = {} self.maps = [] ## create event detector fcDir = os.path.join(os.path.abspath(os.path.split(__file__)[0]), "detector_fc") self.detector = EventDetector.EventDetector(host, flowchartDir=fcDir, dbIdentity=self.dbIdentity+'.events') ## override some of its elements self.detector.setElement('File Loader', self) self.detector.setElement('Database', self.dbCtrl) ## Create element list, importing some gui elements from event detector elems = self.detector.listElements() self._elements_ = OrderedDict([ ('Database', {'type': 'ctrl', 'object': self.dbCtrl, 'size': (300, 600)}), ('Scatter Plot', {'type': 'ctrl', 'object': self.scatterPlot, 'pos': ('right',), 'size': (700,400)}), ('Canvas', {'type': 'canvas', 'pos': ('above', 'Scatter Plot'), 'size': (700,400), 'allowTransforms': False, 'hideCtrl': True, 'args': {'name': 'Photostim'}}), #('Maps', {'type': 'ctrl', 'pos': ('bottom', 'Database'), 'size': (200,200), 'object': self.mapDBCtrl}), ('Map Opts', {'type': 'ctrl', 'object': self.mapCtrl, 'pos': ('above', 'Database'), 'size': (300,600)}), ('Detection Opts', elems['Detection Opts'].setParams(pos=('above', 'Map Opts'), size= (300,600))), ('File Loader', {'type': 'fileInput', 'size': (300, 300), 'pos': ('above', 'Detection Opts'), 'host': self, 'showFileTree': False}), ('Data Plot', elems['Data Plot'].setParams(pos=('bottom', 'Canvas'), size=(700,200))), ('Filter Plot', elems['Filter Plot'].setParams(pos=('bottom', 'Data Plot'), size=(700,200))), ('Event Table', elems['Output Table'].setParams(pos=('below', 'Filter Plot'), size=(700,200))), ('Stats', {'type': 'dataTree', 'size': (700,200), 'pos': ('below', 'Event Table')}), ]) self.initializeElements() try: ## load default chart self.flowchart.loadFile(os.path.join(flowchartDir, 'default.fc')) except: debug.printExc('Error loading default flowchart:') self.detector.flowchart.sigOutputChanged.connect(self.detectorOutputChanged) self.flowchart.sigOutputChanged.connect(self.analyzerOutputChanged) self.detector.flowchart.sigStateChanged.connect(self.detectorStateChanged) self.flowchart.sigStateChanged.connect(self.analyzerStateChanged) self.recolorBtn.clicked.connect(self.recolor)
def drawFrame(self): if self.hasQuit: return #sys.stdout.write('+') try: ## If we last drew a frame < 1/30s ago, return. t = ptime.time() if (self.lastDrawTime is not None) and (t - self.lastDrawTime < .033333): #sys.stdout.write('-') return ## if there is no new frame and no controls have changed, just exit if not self.updateFrame and self.nextFrame is None: #sys.stdout.write('-') return self.updateFrame = False ## If there are no new frames and no previous frames, then there is nothing to draw. if self.currentFrame is None and self.nextFrame is None: #sys.stdout.write('-') return prof = Profiler() ## We will now draw a new frame (even if the frame is unchanged) if self.lastDrawTime is not None: fps = 1.0 / (t - self.lastDrawTime) self.ui.displayFpsLabel.setValue(fps) self.lastDrawTime = t prof() ## Handle the next available frame, if there is one. if self.nextFrame is not None: self.currentFrame = self.nextFrame self.nextFrame = None data = self.currentFrame.data() info = self.currentFrame.info() prof() ## divide the background out of the current frame if needed if self.ui.divideBgBtn.isChecked(): bg = self.getBackgroundFrame() if bg is not None and bg.shape == data.shape: data = data / bg elif self.ui.subtractBgBtn.isChecked(): bg = self.getBackgroundFrame() if bg is not None and bg.shape == data.shape: data = data - bg prof() ## Set new levels if auto gain is enabled if self.ui.btnAutoGain.isChecked(): cw = self.ui.spinAutoGainCenterWeight.value() (w,h) = data.shape center = data[w/2.-w/6.:w/2.+w/6., h/2.-h/6.:h/2.+h/6.] minVal = data.min() * (1.0-cw) + center.min() * cw maxVal = data.max() * (1.0-cw) + center.max() * cw ## If there is inf/nan in the image, strip it out before computing min/max if any([np.isnan(minVal), np.isinf(minVal), np.isnan(minVal), np.isinf(minVal)]): nanMask = np.isnan(data) infMask = np.isinf(data) valid = data[~nanMask * ~infMask] minVal = valid.min() * (1.0-cw) + center.min() * cw maxVal = valid.max() * (1.0-cw) + center.max() * cw ## Smooth min/max range to avoid noise if self.lastMinMax is None: minVal = minVal maxVal = maxVal else: s = 1.0 - 1.0 / (self.ui.spinAutoGainSpeed.value()+1.0) minVal = self.lastMinMax[0] * s + minVal * (1.0-s) maxVal = self.lastMinMax[1] * s + maxVal * (1.0-s) self.lastMinMax = [minVal, maxVal] ## and convert fraction of previous range into new levels bl = self.autoGainLevels[0] * (maxVal-minVal) + minVal wl = self.autoGainLevels[1] * (maxVal-minVal) + minVal self.ignoreLevelChange = True try: self.ui.histogram.setLevels(bl, wl) self.ui.histogram.setHistogramRange(minVal, maxVal, padding=0.05) finally: self.ignoreLevelChange = False prof() ## update image in viewport self.imageItem.updateImage(data)#, levels=[bl, wl]) self.imageItem.setOpacity(self.alpha) self.imageItem.setTransform(self.currentFrame.frameTransform().as2D()) prof() ## Update viewport to correct for scope movement/scaling tr = pg.SRTTransform(self.currentFrame.cameraTransform()) self.updateTransform(tr) self.imageItemGroup.setTransform(tr) prof() prof() prof.finish() except: printExc('Error while drawing new frames:') finally: pass
def _convertDB(self, dbFile, version): ## Convert datbase dbFile from version to the latest version newFileName = dbFile+"version_upgrade" if os.path.exists(newFileName): raise Exception("A .version_upgrade for %s already exists. Please delete or rename it" %dbFile) if version is None: prog = ProgressDialog("Converting database...") from AnalysisDatabase_ver0 import AnalysisDatabase as AnalysisDatabaseOld oldDb = AnalysisDatabaseOld(dbFile) newDb = AnalysisDatabase(newFileName, self.dataModel(), oldDb.baseDir()) dirTypes = ['Day', 'Experiment', 'Slice', 'Cell', 'Site', 'Protocol', 'ProtocolSequence'] print oldDb.listTables() for table in dirTypes: if not oldDb.hasTable(table): continue for rec in oldDb.select(table): dh = oldDb.baseDir()[rec['Dir']] try: newDb.addDir(dh) except: print "Can't add directory %s from old DB:" % dh.name() debug.printExc() total = len(oldDb.select('Photostim_events')) + len(oldDb.select('Photostim_sites')) n=0 for table in ['Photostim_events', 'Photostim_sites', 'Photostim_events2', 'Photostim_sites2']: if prog.wasCanceled(): break if not oldDb.hasTable(table): continue schema = oldDb.tableSchema(table) ## SourceDir -> ProtocolSequenceDir type='directory:ProtocolSequence' del schema['SourceDir'] schema['ProtocolSequenceDir'] = 'directory:ProtocolSequence' ## add column ProtocolDir schema['ProtocolDir'] = 'directory:Protocol' ## SourceFile -> ? type='file' if 'SourceFile' in schema: schema['SourceFile'] = 'file' owner = oldDb.tableOwner(table) newDb.createTable(table, schema, owner=owner) records = oldDb.select(table) for r in records: if prog.wasCanceled(): break ## SourceFile -> convert to filehandle r['SourceFile']= oldDb.getDir('ProtocolSequence', r['SourceDir'])[r['SourceFile']] del r['SourceDir'] ## ProtocolDir, ProtocolSequenceDir -> dirHandles #r['ProtocolSequenceDir'] = oldDb.getDir('ProtocolSequence', r['SourceDir']) r['ProtocolDir'] = r['SourceFile'].parent() r['ProtocolSequenceDir'] = self.dataModel().getParent(r['ProtocolDir'], 'ProtocolSequence') n+=1 prog.setValue(n/total) newDb.insert(table, records) oldDb.close() newDb.close() if not prog.wasCanceled(): os.rename(dbFile, dbFile+'version_upgrade_backup') os.rename(newFileName, dbFile) else: raise Exception("Don't know how to convert from version %s" % str(version))
def _convertDB(self, dbFile, version): ## Convert datbase dbFile from version to the latest version newFileName = dbFile + "version_upgrade" if os.path.exists(newFileName): raise Exception("A .version_upgrade for %s already exists. Please delete or rename it" % dbFile) if version is None: prog = ProgressDialog("Converting database...") from AnalysisDatabase_ver0 import AnalysisDatabase as AnalysisDatabaseOld oldDb = AnalysisDatabaseOld(dbFile) newDb = AnalysisDatabase(newFileName, self.dataModel(), oldDb.baseDir()) dirTypes = ["Day", "Experiment", "Slice", "Cell", "Site", "Protocol", "ProtocolSequence"] print oldDb.listTables() for table in dirTypes: if not oldDb.hasTable(table): continue for rec in oldDb.select(table): dh = oldDb.baseDir()[rec["Dir"]] try: newDb.addDir(dh) except: print "Can't add directory %s from old DB:" % dh.name() debug.printExc() total = len(oldDb.select("Photostim_events")) + len(oldDb.select("Photostim_sites")) n = 0 for table in ["Photostim_events", "Photostim_sites", "Photostim_events2", "Photostim_sites2"]: if prog.wasCanceled(): break if not oldDb.hasTable(table): continue schema = oldDb.tableSchema(table) ## SourceDir -> ProtocolSequenceDir type='directory:ProtocolSequence' del schema["SourceDir"] schema["ProtocolSequenceDir"] = "directory:ProtocolSequence" ## add column ProtocolDir schema["ProtocolDir"] = "directory:Protocol" ## SourceFile -> ? type='file' if "SourceFile" in schema: schema["SourceFile"] = "file" owner = oldDb.tableOwner(table) newDb.createTable(table, schema, owner=owner) records = oldDb.select(table) for r in records: if prog.wasCanceled(): break ## SourceFile -> convert to filehandle r["SourceFile"] = oldDb.getDir("ProtocolSequence", r["SourceDir"])[r["SourceFile"]] del r["SourceDir"] ## ProtocolDir, ProtocolSequenceDir -> dirHandles # r['ProtocolSequenceDir'] = oldDb.getDir('ProtocolSequence', r['SourceDir']) r["ProtocolDir"] = r["SourceFile"].parent() r["ProtocolSequenceDir"] = self.dataModel().getParent(r["ProtocolDir"], "ProtocolSequence") n += 1 prog.setValue(n / total) newDb.insert(table, records) oldDb.close() newDb.close() if not prog.wasCanceled(): os.rename(dbFile, dbFile + "version_upgrade_backup") os.rename(newFileName, dbFile) else: raise Exception("Don't know how to convert from version %s" % str(version))
def __init__(self, host): AnalysisModule.__init__(self, host) flowchartDir = os.path.join( os.path.abspath(os.path.split(__file__)[0]), "flowcharts") self.flowchart = Flowchart(filePath=flowchartDir) self.flowchart.addInput('dataIn') self.flowchart.addOutput('results') self.flowchart.outputNode._allowAddInput = False ## make sure all data is coming out of output['results'] try: ## load default chart self.flowchart.loadFile(os.path.join(flowchartDir, 'default.fc')) except: debug.printExc('Error loading default flowchart:') tables = OrderedDict([(self.dbIdentity + '.traces', 'TimecourseAnalyzer_traces')]) self.dbGui = DatabaseGui(dm=host.dataManager(), tables=tables) self.ctrl = Qt.QWidget() self.ctrl.setLayout(Qt.QVBoxLayout()) self.analyzeBtn = Qt.QPushButton('Analyze') self.storeToDBBtn = Qt.QPushButton('Store to DB') self.ctrl.layout().addWidget(self.analyzeBtn) self.ctrl.layout().addWidget(self.storeToDBBtn) self._elements_ = OrderedDict([('Database', { 'type': 'ctrl', 'object': self.dbGui, 'size': (100, 100) }), ('Analysis Options', { 'type': 'ctrl', 'object': self.flowchart.widget(), 'pos': ('above', 'Database'), 'size': (100, 400) }), ('File Loader', { 'type': 'fileInput', 'size': (100, 100), 'pos': ('above', 'Analysis Options'), 'host': self }), ('Experiment Plot', { 'type': 'plot', 'pos': ('right', 'File Loader'), 'size': (400, 100) }), ('Traces Plot', { 'type': 'plot', 'pos': ('bottom', 'Experiment Plot'), 'size': (400, 200) }), ('Results Plot', { 'type': 'plot', 'pos': ('bottom', 'Traces Plot'), 'size': (400, 200) }), ('Results Table', { 'type': 'table', 'pos': ('bottom', 'Traces Plot'), 'size': (400, 200) }), ('Store Ctrl', { 'type': 'ctrl', 'object': self.ctrl, 'size': (100, 100), 'pos': ('bottom', 'File Loader') })]) self.initializeElements() self.fileLoader = self.getElement('File Loader', create=True) self.exptPlot = self.getElement('Experiment Plot', create=True) self.tracesPlot = self.getElement('Traces Plot', create=True) self.resultsTable = self.getElement('Results Table', create=True) self.resultsTable.setSortingEnabled(False) self.resultsPlot = self.getElement('Results Plot', create=True) self.resultsPlot.getViewBox().setXLink(self.exptPlot.getViewBox( )) ## link the x-axes of the exptPlot and the resultsPlot ### initialize variables self.expStart = 0 self.traces = np.array([], dtype=[('timestamp', float), ('data', object), ('fileHandle', object), ('results', object)]) self.files = [] self.traceSelectRgn = pg.LinearRegionItem() self.traceSelectRgn.setRegion([0, 60]) self.exptPlot.addItem(self.traceSelectRgn) self.traceSelectRgn.sigRegionChanged.connect(self.updateTracesPlot) #self.traceSelectRgn.sigRegionChangeFinished.connect(self.updateAnalysis) #self.flowchart.sigOutputChanged.connect(self.flowchartOutputChanged) #self.addRegionParam = pg.parametertree.Parameter.create(name="Add Region", type='action') #self.paramTree.addParameters(self.addRegionParam) #self.addRegionParam.sigActivated.connect(self.newRegionRequested) self.analyzeBtn.clicked.connect(self.analyzeBtnClicked) self.storeToDBBtn.clicked.connect(self.storeToDBBtnClicked) self.flowchart.sigChartLoaded.connect(self.connectPlots) self.fileLoader.sigClearRequested.connect(self.clearFilesRequested)
def recordingFailed(self): self.endStack() printExc("Recording failed! See console for error message.")
def __init__(self, host): AnalysisModule.__init__(self, host) if self.dataModel is None: raise Exception( "Photostim analysis module requires a data model, but none is loaded yet." ) self.dbIdentity = "Photostim" ## how we identify to the database; this determines which tables we own self.selectedSpot = None ## setup analysis flowchart modPath = os.path.abspath(os.path.split(__file__)[0]) flowchartDir = os.path.join(modPath, "analysis_fc") self.flowchart = Flowchart(filePath=flowchartDir) self.flowchart.addInput('events') self.flowchart.addInput('regions') self.flowchart.addInput('fileHandle') self.flowchart.addOutput('dataOut') self.analysisCtrl = self.flowchart.widget() ## color mapper self.mapper = ColorMapper.ColorMapper( filePath=os.path.join(modPath, "colormaps")) self.mapCtrl = QtGui.QWidget() self.mapLayout = QtGui.QVBoxLayout() self.mapCtrl.setLayout(self.mapLayout) self.mapLayout.splitter = QtGui.QSplitter() self.mapLayout.splitter.setOrientation(QtCore.Qt.Vertical) self.mapLayout.splitter.setContentsMargins(0, 0, 0, 0) self.mapLayout.addWidget(self.mapLayout.splitter) self.mapLayout.splitter.addWidget(self.analysisCtrl) #self.mapLayout.splitter.addWidget(QtGui.QSplitter()) self.mapLayout.splitter.addWidget(self.mapper) #self.mapLayout.splitter.addWidget(self.recolorBtn) self.recolorLayout = QtGui.QHBoxLayout() self.recolorWidget = QtGui.QWidget() self.mapLayout.splitter.addWidget(self.recolorWidget) self.recolorWidget.setLayout(self.recolorLayout) self.recolorBtn = QtGui.QPushButton('Recolor') self.recolorLayout.addWidget(self.recolorBtn) self.recolorParallelCheck = QtGui.QCheckBox('Parallel') self.recolorParallelCheck.setChecked(True) self.recolorLayout.addWidget(self.recolorParallelCheck) ## scatter plot self.scatterPlot = ScatterPlotter() self.scatterPlot.sigClicked.connect(self.scatterPlotClicked) ## setup map DB ctrl self.dbCtrl = DBCtrl(self, self.dbIdentity) ## storage for map data #self.scanItems = {} self.scans = [] #self.seriesScans = {} self.maps = [] ## create event detector fcDir = os.path.join(os.path.abspath(os.path.split(__file__)[0]), "detector_fc") self.detector = EventDetector.EventDetector( host, flowchartDir=fcDir, dbIdentity=self.dbIdentity + '.events') ## override some of its elements self.detector.setElement('File Loader', self) self.detector.setElement('Database', self.dbCtrl) ## Create element list, importing some gui elements from event detector elems = self.detector.listElements() self._elements_ = OrderedDict([ ('Database', { 'type': 'ctrl', 'object': self.dbCtrl, 'size': (300, 600) }), ('Scatter Plot', { 'type': 'ctrl', 'object': self.scatterPlot, 'pos': ('right', ), 'size': (700, 400) }), ('Canvas', { 'type': 'canvas', 'pos': ('above', 'Scatter Plot'), 'size': (700, 400), 'allowTransforms': False, 'hideCtrl': True, 'args': { 'name': 'Photostim' } }), #('Maps', {'type': 'ctrl', 'pos': ('bottom', 'Database'), 'size': (200,200), 'object': self.mapDBCtrl}), ('Map Opts', { 'type': 'ctrl', 'object': self.mapCtrl, 'pos': ('above', 'Database'), 'size': (300, 600) }), ('Detection Opts', elems['Detection Opts'].setParams(pos=('above', 'Map Opts'), size=(300, 600))), ('File Loader', { 'type': 'fileInput', 'size': (300, 300), 'pos': ('above', 'Detection Opts'), 'host': self, 'showFileTree': False }), ('Data Plot', elems['Data Plot'].setParams(pos=('bottom', 'Canvas'), size=(700, 200))), ('Filter Plot', elems['Filter Plot'].setParams(pos=('bottom', 'Data Plot'), size=(700, 200))), ('Event Table', elems['Output Table'].setParams(pos=('below', 'Filter Plot'), size=(700, 200))), ('Stats', { 'type': 'dataTree', 'size': (700, 200), 'pos': ('below', 'Event Table') }), ]) self.initializeElements() try: ## load default chart self.flowchart.loadFile(os.path.join(flowchartDir, 'default.fc')) except: debug.printExc('Error loading default flowchart:') self.detector.flowchart.sigOutputChanged.connect( self.detectorOutputChanged) self.flowchart.sigOutputChanged.connect(self.analyzerOutputChanged) self.detector.flowchart.sigStateChanged.connect( self.detectorStateChanged) self.flowchart.sigStateChanged.connect(self.analyzerStateChanged) self.recolorBtn.clicked.connect(self.recolor)
def __init__(self, image=None, **opts): """ CanvasItem displaying an image. The image may be 2 or 3-dimensional. Options: image: May be a fileHandle, ndarray, or GraphicsItem. handle: May optionally be specified in place of image """ ## If no image was specified, check for a file handle.. if image is None: image = opts.get('handle', None) item = None self.data = None self.currentT = None if isinstance(image, QtGui.QGraphicsItem): item = image elif isinstance(image, np.ndarray): self.data = image elif isinstance(image, DataManager.FileHandle): opts['handle'] = image self.handle = image self.data = self.handle.read() if 'name' not in opts: opts['name'] = self.handle.shortName() try: if 'transform' in self.handle.info(): tr = pg.SRTTransform3D(self.handle.info()['transform']) tr = pg.SRTTransform(tr) ## convert to 2D opts['pos'] = tr.getTranslation() opts['scale'] = tr.getScale() opts['angle'] = tr.getRotation() else: ## check for older info formats if 'imagePosition' in self.handle.info(): opts['scale'] = self.handle.info()['pixelSize'] opts['pos'] = self.handle.info()['imagePosition'] elif 'Downsample' in self.handle.info(): ### Needed to support an older format stored by 2p imager if 'pixelSize' in self.handle.info(): opts['scale'] = self.handle.info()['pixelSize'] if 'microscope' in self.handle.info(): m = self.handle.info()['microscope'] print 'm: ', m print 'mpos: ', m['position'] opts['pos'] = m['position'][0:2] else: info = self.data._info[-1] opts['pos'] = info.get('imagePosition', None) elif hasattr(self.data, '_info'): info = self.data._info[-1] opts['scale'] = info.get('pixelSize', None) opts['pos'] = info.get('imagePosition', None) else: opts['defaultUserTransform'] = {'scale': (1e-5, 1e-5)} opts['scalable'] = True except: debug.printExc( 'Error reading transformation for image file %s:' % image.name()) if item is None: item = pg.ImageItem() CanvasItem.__init__(self, item, **opts) self.histogram = pg.PlotWidget() self.blockHistogram = False self.histogram.setMaximumHeight(100) self.levelRgn = pg.LinearRegionItem() self.histogram.addItem(self.levelRgn) self.updateHistogram(autoLevels=True) # addWidget arguments: row, column, rowspan, colspan self.layout.addWidget(self.histogram, self.layout.rowCount(), 0, 1, 3) self.timeSlider = QtGui.QSlider(QtCore.Qt.Horizontal) #self.timeSlider.setMinimum(0) #self.timeSlider.setMaximum(self.data.shape[0]-1) self.layout.addWidget(self.timeSlider, self.layout.rowCount(), 0, 1, 3) self.timeSlider.valueChanged.connect(self.timeChanged) self.timeSlider.sliderPressed.connect(self.timeSliderPressed) self.timeSlider.sliderReleased.connect(self.timeSliderReleased) thisRow = self.layout.rowCount() self.edgeBtn = QtGui.QPushButton('Edge') self.edgeBtn.clicked.connect(self.edgeClicked) self.layout.addWidget(self.edgeBtn, thisRow, 0, 1, 1) self.meanBtn = QtGui.QPushButton('Mean') self.meanBtn.clicked.connect(self.meanClicked) self.layout.addWidget(self.meanBtn, thisRow + 1, 0, 1, 1) self.tvBtn = QtGui.QPushButton('tv denoise') self.tvBtn.clicked.connect(self.tvClicked) self.layout.addWidget(self.tvBtn, thisRow + 2, 0, 1, 1) self.maxBtn = QtGui.QPushButton('Max no Filter') self.maxBtn.clicked.connect(self.maxClicked) self.layout.addWidget(self.maxBtn, thisRow, 1, 1, 1) self.maxBtn2 = QtGui.QPushButton('Max w/Gaussian') self.maxBtn2.clicked.connect(self.max2Clicked) self.layout.addWidget(self.maxBtn2, thisRow + 1, 1, 1, 1) self.maxMedianBtn = QtGui.QPushButton('Max w/Median') self.maxMedianBtn.clicked.connect(self.maxMedianClicked) self.layout.addWidget(self.maxMedianBtn, thisRow + 2, 1, 1, 1) self.filterOrder = QtGui.QComboBox() self.filterLabel = QtGui.QLabel('Order') for n in range(1, 11): self.filterOrder.addItem("%d" % n) self.layout.addWidget(self.filterLabel, thisRow + 3, 2, 1, 1) self.layout.addWidget(self.filterOrder, thisRow + 3, 3, 1, 1) self.zPlanes = QtGui.QComboBox() self.zPlanesLabel = QtGui.QLabel('# planes') for s in ['All', '1', '2', '3', '4', '5']: self.zPlanes.addItem("%s" % s) self.layout.addWidget(self.zPlanesLabel, thisRow + 3, 0, 1, 1) self.layout.addWidget(self.zPlanes, thisRow + 3, 1, 1, 1) ## controls that only appear if there is a time axis self.timeControls = [ self.timeSlider, self.edgeBtn, self.maxBtn, self.meanBtn, self.maxBtn2, self.maxMedianBtn, self.filterOrder, self.zPlanes ] if self.data is not None: self.updateImage(self.data) self.graphicsItem().sigImageChanged.connect(self.updateHistogram) self.levelRgn.sigRegionChanged.connect(self.levelsChanged) self.levelRgn.sigRegionChangeFinished.connect( self.levelsChangeFinished)
def __init__(self, image=None, **opts): """ CanvasItem displaying an image. The image may be 2 or 3-dimensional. Options: image: May be a fileHandle, ndarray, or GraphicsItem. handle: May optionally be specified in place of image """ ## If no image was specified, check for a file handle.. if image is None: image = opts.get('handle', None) item = None self.data = None self.currentT = None if isinstance(image, QtGui.QGraphicsItem): item = image elif isinstance(image, np.ndarray): self.data = image elif isinstance(image, DataManager.FileHandle): opts['handle'] = image self.handle = image self.data = self.handle.read() if 'name' not in opts: opts['name'] = self.handle.shortName() try: if 'transform' in self.handle.info(): tr = pg.SRTTransform3D(self.handle.info()['transform']) tr = pg.SRTTransform(tr) ## convert to 2D opts['pos'] = tr.getTranslation() opts['scale'] = tr.getScale() opts['angle'] = tr.getRotation() else: ## check for older info formats if 'imagePosition' in self.handle.info(): opts['scale'] = self.handle.info()['pixelSize'] opts['pos'] = self.handle.info()['imagePosition'] elif 'Downsample' in self.handle.info(): ### Needed to support an older format stored by 2p imager if 'pixelSize' in self.handle.info(): opts['scale'] = self.handle.info()['pixelSize'] if 'microscope' in self.handle.info(): m = self.handle.info()['microscope'] print 'm: ',m print 'mpos: ', m['position'] opts['pos'] = m['position'][0:2] else: info = self.data._info[-1] opts['pos'] = info.get('imagePosition', None) elif hasattr(self.data, '_info'): info = self.data._info[-1] opts['scale'] = info.get('pixelSize', None) opts['pos'] = info.get('imagePosition', None) else: opts['defaultUserTransform'] = {'scale': (1e-5, 1e-5)} opts['scalable'] = True except: debug.printExc('Error reading transformation for image file %s:' % image.name()) if item is None: item = pg.ImageItem() CanvasItem.__init__(self, item, **opts) self.histogram = pg.PlotWidget() self.blockHistogram = False self.histogram.setMaximumHeight(100) self.levelRgn = pg.LinearRegionItem() self.histogram.addItem(self.levelRgn) self.updateHistogram(autoLevels=True) # addWidget arguments: row, column, rowspan, colspan self.layout.addWidget(self.histogram, self.layout.rowCount(), 0, 1, 3) self.timeSlider = QtGui.QSlider(QtCore.Qt.Horizontal) #self.timeSlider.setMinimum(0) #self.timeSlider.setMaximum(self.data.shape[0]-1) self.layout.addWidget(self.timeSlider, self.layout.rowCount(), 0, 1, 3) self.timeSlider.valueChanged.connect(self.timeChanged) self.timeSlider.sliderPressed.connect(self.timeSliderPressed) self.timeSlider.sliderReleased.connect(self.timeSliderReleased) thisRow = self.layout.rowCount() self.edgeBtn = QtGui.QPushButton('Edge') self.edgeBtn.clicked.connect(self.edgeClicked) self.layout.addWidget(self.edgeBtn, thisRow, 0, 1, 1) self.meanBtn = QtGui.QPushButton('Mean') self.meanBtn.clicked.connect(self.meanClicked) self.layout.addWidget(self.meanBtn, thisRow+1, 0, 1, 1) self.tvBtn = QtGui.QPushButton('tv denoise') self.tvBtn.clicked.connect(self.tvClicked) self.layout.addWidget(self.tvBtn, thisRow+2, 0, 1, 1) self.maxBtn = QtGui.QPushButton('Max no Filter') self.maxBtn.clicked.connect(self.maxClicked) self.layout.addWidget(self.maxBtn, thisRow, 1, 1, 1) self.maxBtn2 = QtGui.QPushButton('Max w/Gaussian') self.maxBtn2.clicked.connect(self.max2Clicked) self.layout.addWidget(self.maxBtn2, thisRow+1, 1, 1, 1) self.maxMedianBtn = QtGui.QPushButton('Max w/Median') self.maxMedianBtn.clicked.connect(self.maxMedianClicked) self.layout.addWidget(self.maxMedianBtn, thisRow+2, 1, 1, 1) self.filterOrder = QtGui.QComboBox() self.filterLabel = QtGui.QLabel('Order') for n in range(1,11): self.filterOrder.addItem("%d" % n) self.layout.addWidget(self.filterLabel, thisRow+3, 2, 1, 1) self.layout.addWidget(self.filterOrder, thisRow+3, 3, 1, 1) self.zPlanes = QtGui.QComboBox() self.zPlanesLabel = QtGui.QLabel('# planes') for s in ['All', '1', '2', '3', '4', '5']: self.zPlanes.addItem("%s" % s) self.layout.addWidget(self.zPlanesLabel, thisRow+3, 0, 1, 1) self.layout.addWidget(self.zPlanes, thisRow + 3, 1, 1, 1) ## controls that only appear if there is a time axis self.timeControls = [self.timeSlider, self.edgeBtn, self.maxBtn, self.meanBtn, self.maxBtn2, self.maxMedianBtn, self.filterOrder, self.zPlanes] if self.data is not None: self.updateImage(self.data) self.graphicsItem().sigImageChanged.connect(self.updateHistogram) self.levelRgn.sigRegionChanged.connect(self.levelsChanged) self.levelRgn.sigRegionChangeFinished.connect(self.levelsChangeFinished)
def run(self): self.stopThread = False #self.sp = serial.Serial(int(self.port), baudrate=self.baud, bytesize=serial.EIGHTBITS) #time.sleep(3) ## Wait a few seconds for the mouse to say hello ## clear buffer before starting #if self.sp.inWaiting() > 0: #print "Discarding %d bytes" % self.sp.inWaiting() #self.sp.read(self.sp.inWaiting()) #import wingdbstub print " Starting MP285 thread: 0x%x" % int(QtCore.QThread.currentThreadId()) #import sip #print " also known as 0x%x" % sip.unwrapinstance(self) velocity = np.array([0,0,0]) pos = [0,0,0] try: self.getImmediatePos() monitor = True except: debug.printExc("Sutter MP285: Cannot determine position:") monitor = False while True: try: ## Lock and copy state to local variables with self.lock: update = self.update self.update = False limits = deepcopy(self.limits) maxSpeed = self.maxSpeed newVelocity = np.array(self.velocity[:]) resolution = self.resolution limitChanged = self.limitChanged self.limitChanged = False ## if limits have changed, inform the device if monitor and limitChanged: ## monitor is only true if this is a customized device with limit checking self.sendLimits() ## If requested velocity is different from the current velocity, handle that. if np.any(newVelocity != velocity): speed = np.clip(np.sum(newVelocity**2)**0.5, 0., 1.) ## should always be 0.0-1.0 #print "new velocity:", newVelocity, "speed:", speed if speed == 0: nv = np.array([0,0,0]) else: nv = newVelocity/speed speed = np.clip(speed, 0, maxSpeed) #print "final speed:", speed ## stop current move, get position, start new move #print "stop.." self.stopMove() #print "stop done." #print "getpos..." pos1 = self.readPosition() if pos1 is not None: if speed > 0: #print " set new velocity" self.writeVelocity(speed, nv, limits=limits, pos=pos1, resolution=resolution) #print " done" ## report current position velocity = newVelocity #print "done" ## If velocity is 0, we can do some position checks if np.all(velocity == 0): newPos = None if update: newPos = self.readPosition() elif monitor: newPos = self.getImmediatePos() if newPos is not None: ## If position has changed, emit a signal. change = [newPos[i] - pos[i] for i in range(len(newPos))] pos = newPos if any(change): #self.emit(QtCore.SIGNAL('positionChanged'), {'rel': change, 'abs': self.pos}) self.sigPositionChanged.emit({'rel': change, 'abs': pos}) else: ## moving; make a guess about the current position pass except: pass debug.printExc("Error in MP285 thread:") self.lock.lock() if self.stopThread: self.lock.unlock() break self.lock.unlock() time.sleep(0.02) self.mp285.close()
def __init__(self, dm, config, name): # Generate config to use for DAQ daqConfig = {} for ch in ["GainChannel", "LPFChannel", "ModeChannel"]: if ch not in config: continue daqConfig[ch] = config[ch].copy() # if 'GainChannel' in config: # daqConfig['gain'] = {'type': 'ai', 'channel': config['GainChannel']} # if 'LPFChannel' in config: # daqConfig['LPF'] = {'type': 'ai', 'channel': config['LPFChannel'], 'units': 'Hz'} if "ScaledSignal" in config: # daqConfig['primary'] = {'type': 'ai', 'channel': config['ScaledSignal']} daqConfig["primary"] = config["ScaledSignal"] if config["ScaledSignal"].get("type", None) != "ai": raise Exception("AxoPatch200: ScaledSignal configuration must have type:'ai'") if "Command" in config: # daqConfig['command'] = {'type': 'ao', 'channel': config['Command']} daqConfig["command"] = config["Command"] if config["Command"].get("type", None) != "ao": raise Exception("AxoPatch200: ScaledSignal configuration must have type:'ao'") ## Note that both of these channels can be present, but we will only ever record from one at a time. ## Usually, we'll record from "I OUTPUT" in current clamp and "10 Vm OUTPUT" in voltage clamp. if "SecondaryVCSignal" in config: self.hasSecondaryChannel = True # daqConfig['secondary'] = {'type': 'ai', 'channel': config['SecondaryVCSignal']} daqConfig["secondary"] = config["SecondaryVCSignal"] if config["SecondaryVCSignal"].get("type", None) != "ai": raise Exception("AxoPatch200: SecondaryVCSignal configuration must have type:'ai'") elif "SecondaryICSignal" in config: self.hasSecondaryChannel = True # daqConfig['secondary'] = {'type': 'ai', 'channel': config['SecondaryICSignal']} daqConfig["secondary"] = config["SecondaryICSignal"] if config["SecondaryICSignal"].get("type", None) != "ai": raise Exception("AxoPatch200: SecondaryICSignal configuration must have type:'ai'") else: self.hasSecondaryChannel = False self.holding = {"vc": config.get("vcHolding", -0.05), "ic": config.get("icHolding", 0.0)} self.config = config self.modeLock = Mutex(Mutex.Recursive) ## protects self.mdCanceled self.devLock = Mutex(Mutex.Recursive) ## protects self.holding, possibly self.config, ..others, perhaps? self.mdCanceled = False DAQGeneric.__init__(self, dm, daqConfig, name) self.modeDialog = QtGui.QMessageBox() self.modeDialog.hide() self.modeDialog.setModal(False) self.modeDialog.setWindowTitle("Mode Switch Request") self.modeDialog.addButton(self.modeDialog.Cancel) self.modeDialog.buttonClicked.connect(self.modeDialogClicked) self.sigShowModeDialog.connect(self.showModeDialog) self.sigHideModeDialog.connect(self.hideModeDialog) try: self.setHolding() except: printExc("Error while setting holding value:") dm.declareInterface(name, ["clamp"], self)