class PatchWindow(QtGui.QMainWindow): sigWindowClosed = QtCore.Signal(object) def __init__(self, dm, clampName): QtGui.QMainWindow.__init__(self) self.setWindowTitle(clampName) self.startTime = None self.redrawCommand = 1 self.analysisItems = { 'inputResistance': u'Ω', 'accessResistance': u'Ω', 'capacitance': 'F', 'restingPotential': 'V', 'restingCurrent': 'A', 'fitError': '' } self.params = { 'mode': 'vc', 'rate': 400000, 'downsample': 10, 'cycleTime': .2, 'recordTime': 0.1, 'delayTime': 0.03, 'pulseTime': 0.05, 'icPulse': -30e-12, 'vcPulse': -10e-3, 'icHolding': 0, 'vcHolding': -65e-3, 'icHoldingEnabled': False, 'icPulseEnabled': True, 'vcHoldingEnabled': False, 'vcPulseEnabled': True, 'drawFit': True, 'average': 1, } self.paramLock = Mutex(QtCore.QMutex.Recursive) self.manager = dm self.clampName = clampName self.thread = PatchThread(self) self.cw = QtGui.QWidget() self.setCentralWidget(self.cw) self.ui = Ui_Form() self.ui.setupUi(self.cw) #self.logBtn = LogButton("Log") #self.statusBar().addPermanentWidget(self.logBtn) self.setStatusBar(StatusBar()) self.stateFile = os.path.join('modules', self.clampName + '_ui.cfg') uiState = Manager.getManager().readConfigFile(self.stateFile) if 'geometry' in uiState: geom = QtCore.QRect(*uiState['geometry']) self.setGeometry(geom) if 'window' in uiState: ws = QtCore.QByteArray.fromPercentEncoding(uiState['window']) self.restoreState(ws) self.ui.splitter_2.setSizes([self.width()/4, self.width()*3./4.]) self.plots = {} for k in self.analysisItems: p = PlotWidget() p.setLabel('left', text=k, units=self.analysisItems[k]) self.ui.plotLayout.addWidget(p) self.plots[k] = p #irp = self.plots['inputResistance'] #irp.setManualYScale() #irp.setYLog(True) #irp.setYRange(1e6, 1e11) self.ui.icPulseSpin.setOpts(dec=True, step=1, minStep=1e-12, bounds=[None,None], siPrefix=True, suffix='A') self.ui.vcPulseSpin.setOpts(dec=True, step=1, minStep=1e-3, bounds=[None,None], siPrefix=True, suffix='V') self.ui.icHoldSpin.setOpts(dec=True, step=1, minStep=1e-12, bounds=[None,None], siPrefix=True, suffix='A') self.ui.vcHoldSpin.setOpts(dec=True, step=1, minStep=1e-3, bounds=[None,None], siPrefix=True, suffix='V') self.ui.cycleTimeSpin.setOpts(dec=True, step=1, minStep=1e-6, bounds=[0,None], siPrefix=True, suffix='s') self.ui.pulseTimeSpin.setOpts(dec=True, step=1, minStep=1e-6, bounds=[0,1.], siPrefix=True, suffix='s') self.ui.delayTimeSpin.setOpts(dec=True, step=1, minStep=1e-6, bounds=[0,1.], siPrefix=True, suffix='s') self.stateGroup = WidgetGroup([ (self.ui.icPulseSpin, 'icPulse'), (self.ui.vcPulseSpin, 'vcPulse'), (self.ui.icHoldSpin, 'icHolding'), (self.ui.vcHoldSpin, 'vcHolding'), (self.ui.icPulseCheck, 'icPulseEnabled'), (self.ui.vcPulseCheck, 'vcPulseEnabled'), (self.ui.icHoldCheck, 'icHoldingEnabled'), (self.ui.vcHoldCheck, 'vcHoldingEnabled'), (self.ui.cycleTimeSpin, 'cycleTime'), (self.ui.pulseTimeSpin, 'pulseTime'), (self.ui.delayTimeSpin, 'delayTime'), (self.ui.drawFitCheck, 'drawFit'), (self.ui.averageSpin, 'average'), ]) self.stateGroup.setState(self.params) self.ui.patchPlot.setLabel('left', text='Primary', units='A') self.patchCurve = self.ui.patchPlot.plot(pen=QtGui.QPen(QtGui.QColor(200, 200, 200))) self.patchFitCurve = self.ui.patchPlot.plot(pen=QtGui.QPen(QtGui.QColor(0, 100, 200))) self.ui.commandPlot.setLabel('left', text='Command', units='V') self.commandCurve = self.ui.commandPlot.plot(pen=QtGui.QPen(QtGui.QColor(200, 200, 200))) self.ui.startBtn.clicked.connect(self.startClicked) self.ui.recordBtn.clicked.connect(self.recordClicked) self.ui.bathModeBtn.clicked.connect(self.bathMode) self.ui.patchModeBtn.clicked.connect(self.patchMode) self.ui.cellModeBtn.clicked.connect(self.cellMode) self.ui.monitorModeBtn.clicked.connect(self.monitorMode) self.ui.resetBtn.clicked.connect(self.resetClicked) self.thread.finished.connect(self.threadStopped) self.thread.sigNewFrame.connect(self.handleNewFrame) self.ui.vcModeRadio.toggled.connect(self.updateParams) self.stateGroup.sigChanged.connect(self.updateParams) ## Configure analysis plots, curves, and data arrays self.analysisCurves = {} self.analysisData = {'time': []} for n in self.analysisItems: w = getattr(self.ui, n+'Check') w.clicked.connect(self.showPlots) p = self.plots[n] self.analysisCurves[n] = p.plot(pen=QtGui.QPen(QtGui.QColor(200, 200, 200))) for suf in ['', 'Std']: self.analysisData[n+suf] = [] self.showPlots() self.updateParams() self.show() self.bathMode() def quit(self): #print "Stopping patch thread.." geom = self.geometry() uiState = {'window': str(self.saveState().toPercentEncoding()), 'geometry': [geom.x(), geom.y(), geom.width(), geom.height()]} Manager.getManager().writeConfigFile(uiState, self.stateFile) self.thread.stop(block=True) #print "Patch thread exited; module quitting." def closeEvent(self, ev): self.quit() self.sigWindowClosed.emit(self) def bathMode(self): self.ui.vcPulseCheck.setChecked(True) self.ui.vcHoldCheck.setChecked(False) self.ui.vcModeRadio.setChecked(True) self.ui.cycleTimeSpin.setValue(0.2) self.ui.pulseTimeSpin.setValue(10e-3) self.ui.delayTimeSpin.setValue(10e-3) self.ui.averageSpin.setValue(1) def patchMode(self): self.ui.vcPulseCheck.setChecked(True) self.ui.vcHoldCheck.setChecked(True) self.ui.vcModeRadio.setChecked(True) self.ui.cycleTimeSpin.setValue(0.2) self.ui.pulseTimeSpin.setValue(10e-3) self.ui.delayTimeSpin.setValue(10e-3) self.ui.averageSpin.setValue(1) def cellMode(self): self.ui.icPulseCheck.setChecked(True) self.ui.icModeRadio.setChecked(True) self.ui.cycleTimeSpin.setValue(250e-3) self.ui.pulseTimeSpin.setValue(150e-3) self.ui.delayTimeSpin.setValue(30e-3) self.ui.averageSpin.setValue(1) def monitorMode(self): self.ui.cycleTimeSpin.setValue(40) self.ui.averageSpin.setValue(5) def showPlots(self): """Show/hide analysis plot widgets""" for n in self.analysisItems: w = getattr(self.ui, n+'Check') p = self.plots[n] if w.isChecked(): p.show() else: p.hide() self.updateAnalysisPlots() def updateParams(self, *args): with self.paramLock: if self.ui.icModeRadio.isChecked(): mode = 'ic' else: mode = 'vc' self.params['mode'] = mode state = self.stateGroup.state() for p in self.params: if p in state: self.params[p] = state[p] self.params['recordTime'] = self.params['delayTime'] *2.0 + self.params['pulseTime'] self.thread.updateParams() self.redrawCommand = 2 ## may need to redraw twice to make sure the update has gone through def recordClicked(self): if self.ui.recordBtn.isChecked(): data = self.makeAnalysisArray() if data.shape[0] == 0: ## no data yet; don't start the file self.storageFile = None return self.newFile(data) else: self.storageFile = None def newFile(self, data): sd = self.storageDir() self.storageFile = sd.writeFile(data, self.clampName, autoIncrement=True, appendAxis='Time', newFile=True) if self.startTime is not None: self.storageFile.setInfo({'startTime': self.startTime}) def storageDir(self): return self.manager.getCurrentDir().getDir('Patch', create=True) #def storageFile(self): #sd = self.storageDir() #return sd.getFile(self.clampName, create=True) def resetClicked(self): self.ui.recordBtn.setChecked(False) self.recordClicked() for n in self.analysisData: self.analysisData[n] = [] self.startTime = None def handleNewFrame(self, frame): prof = Profiler('PatchWindow.handleNewFrame', disabled=True) with self.paramLock: mode = self.params['mode'] data = frame['data'][self.clampName] if mode == 'vc': self.ui.patchPlot.setLabel('left', units='A') else: self.ui.patchPlot.setLabel('left', units='V') prof.mark('1') self.patchCurve.setData(data.xvals('Time'), data['primary']) prof.mark('2') if self.redrawCommand > 0: self.redrawCommand -= 1 #print "set command curve" self.commandCurve.setData(data.xvals('Time'), data['command']) if mode == 'vc': self.ui.commandPlot.setLabel('left', units='V') else: self.ui.commandPlot.setLabel('left', units='A') prof.mark('3') #self.ui.patchPlot.replot() #self.ui.commandPlot.replot() if frame['analysis']['fitTrace'] is not None: self.patchFitCurve.show() self.patchFitCurve.setData(data.xvals('Time'), frame['analysis']['fitTrace']) else: self.patchFitCurve.hide() prof.mark('4') for k in self.analysisItems: if k in frame['analysis']: self.analysisData[k].append(frame['analysis'][k]) prof.mark('5') for r in ['input', 'access']: res = r+'Resistance' label = getattr(self.ui, res+'Label') resistance = frame['analysis'][res] label.setText(siFormat(resistance) + u'Ω') prof.mark('6') self.ui.restingPotentialLabel.setText(siFormat(frame['analysis']['restingPotential'], error=frame['analysis']['restingPotentialStd'], suffix='V')) self.ui.restingCurrentLabel.setText(siFormat(frame['analysis']['restingCurrent'], error=frame['analysis']['restingCurrentStd'], suffix='A')) self.ui.capacitanceLabel.setText('%sF' % siFormat(frame['analysis']['capacitance'])) self.ui.fitErrorLabel.setText('%7.2g' % frame['analysis']['fitError']) prof.mark('7') start = data._info[-1]['DAQ']['command']['startTime'] if self.startTime is None: self.startTime = start if self.ui.recordBtn.isChecked() and self.storageFile is not None: self.storageFile.setInfo({'startTime': self.startTime}) self.analysisData['time'].append(start - self.startTime) prof.mark('8') self.updateAnalysisPlots() prof.mark('9') ## Record to disk if requested. if self.ui.recordBtn.isChecked(): arr = self.makeAnalysisArray(lastOnly=True) #print "appending array", arr.shape if self.storageFile is None: self.newFile(arr) else: arr.write(self.storageFile.name(), appendAxis='Time') prof.mark('10') prof.finish() def makeAnalysisArray(self, lastOnly=False): ## Determine how much of the data to include in this array if lastOnly: sl = slice(-1, None) else: sl = slice(None) ## Generate the meta-info structure info = [ {'name': 'Time', 'values': self.analysisData['time'][sl], 'units': 's'}, {'name': 'Value', 'cols': []} ] for k in self.analysisItems: for s in ['', 'Std']: if len(self.analysisData[k+s]) < 1: continue info[1]['cols'].append({'name': k+s, 'units': self.analysisItems[k]}) ## Create the blank MetaArray data = MetaArray( (len(info[0]['values']), len(info[1]['cols'])), dtype=float, info=info ) ## Fill with data for k in self.analysisItems: for s in ['', 'Std']: if len(self.analysisData[k+s]) < 1: continue try: data[:, k+s] = self.analysisData[k+s][sl] except: print data.shape, data[:, k+s].shape, len(self.analysisData[k+s][sl]) raise return data def updateAnalysisPlots(self): for n in self.analysisItems: p = self.plots[n] if p.isVisible(): self.analysisCurves[n].setData(self.analysisData['time'], self.analysisData[n]) #if len(self.analysisData[n+'Std']) > 0: #self.analysisCurves[p+'Std'].setData(self.analysisData['time'], self.analysisData[n+'Std']) #p.replot() def startClicked(self): if self.ui.startBtn.isChecked(): if not self.thread.isRunning(): self.thread.start() Manager.logMsg("Patch module started.") self.ui.startBtn.setText('Stop') else: self.ui.startBtn.setEnabled(False) self.thread.stop() Manager.logMsg("Patch module stopped.") def threadStopped(self): self.ui.startBtn.setText('Start') self.ui.startBtn.setEnabled(True) self.ui.startBtn.setChecked(False)
class DaqChannelGui(QtGui.QWidget): def __init__(self, parent, name, config, plot, dev, taskRunner, daqName=None): QtGui.QWidget.__init__(self, parent) ## Name of this channel self.name = name ## Parent taskGui object self.taskGui = weakref.ref(parent) ## Configuration for this channel defined in the device configuration file self.config = config self.scale = 1.0 self.units = '' ## The device handle for this channel's DAQGeneric device self.dev = dev ## The task GUI window which contains this object self.taskRunner = weakref.ref(taskRunner) ## Make sure task interface includes our DAQ device if daqName is None: self.daqDev = self.dev.getDAQName(self.name) else: self.daqDev = daqName self.daqUI = self.taskRunner().getDevice(self.daqDev) ## plot widget self.plot = plot self.plot.setDownsampling(ds=True, auto=True, mode='peak') self.plot.setClipToView(True) def postUiInit(self): ## Automatically locate all read/writable widgets and group them together for easy ## save/restore operations self.stateGroup = WidgetGroup(self) self.stateGroup.addWidget(self.plot, name='plot') self.displayCheckChanged() self.ui.displayCheck.stateChanged.connect(self.displayCheckChanged) if 'units' in self.config: self.setUnits(self.config['units']) else: self.setUnits('') def updateTitle(self): self.ui.groupBox.setTitle(self.name + " (%s)" % self.units) def setUnits(self, units): self.units = units for s in self.getSpins(): if isinstance(s, SpinBox): s.setOpts(suffix=units) self.updateTitle() def getSpins(self): return [] def setChildrenVisible(self, obj, vis): for c in obj.children(): if isinstance(c, QtGui.QWidget): c.setVisible(vis) else: self.setChildrenVisible(c, vis) def saveState(self): return self.stateGroup.state() def restoreState(self, state): self.stateGroup.setState(state) if hasattr(self.ui, 'waveGeneratorWidget'): self.ui.waveGeneratorWidget.update() def clearPlots(self): self.plot.clear() self.currentPlot = None def displayCheckChanged(self): if self.stateGroup.state()['displayCheck']: self.plot.show() else: self.plot.hide() def taskStarted(self, params): pass def taskSequenceStarted(self): pass def quit(self): #print "quit DAQGeneric channel", self.name self.plot.close()
class DaqChannelGui(QtGui.QWidget): def __init__(self, parent, name, config, plot, dev, taskRunner, daqName=None): QtGui.QWidget.__init__(self, parent) ## Name of this channel self.name = name ## Parent taskGui object self.taskGui = weakref.ref(parent) ## Configuration for this channel defined in the device configuration file self.config = config self.scale = 1.0 self.units = '' ## The device handle for this channel's DAQGeneric device self.dev = dev ## The task GUI window which contains this object self.taskRunner = weakref.ref(taskRunner) ## Make sure task interface includes our DAQ device if daqName is None: self.daqDev = self.dev.getDAQName(self.name) else: self.daqDev = daqName self.daqUI = self.taskRunner().getDevice(self.daqDev) ## plot widget self.plot = plot self.plot.setDownsampling(ds=True, auto=True, mode='peak') self.plot.setClipToView(True) #plot.setCanvasBackground(QtGui.QColor(0,0,0)) #plot.replot() ## Curves displayed in self.plot #self.plots = [] def postUiInit(self): ## Automatically locate all read/writable widgets and group them together for easy ## save/restore operations self.stateGroup = WidgetGroup(self) self.stateGroup.addWidget(self.plot, name='plot') self.displayCheckChanged() #QtCore.QObject.connect(self.ui.displayCheck, QtCore.SIGNAL('stateChanged(int)'), self.displayCheckChanged) self.ui.displayCheck.stateChanged.connect(self.displayCheckChanged) #QtCore.QObject.connect(self.ui.groupBox, QtCore.SIGNAL('toggled(bool)'), self.groupBoxClicked) self.ui.groupBox.toggled.connect(self.groupBoxClicked) #if 'userScale' in self.config: #self.setScale(self.config['userScale']) #else: #self.setScale(1.0) if 'units' in self.config: self.setUnits(self.config['units']) else: self.setUnits('') def updateTitle(self): if self.ui.groupBox.isChecked(): plus = "" else: plus = "[+] " #units = " (x%s)" % siFormat(self.scale, suffix=self.units) self.ui.groupBox.setTitle(plus + self.name + " (%s)" %self.units) def setUnits(self, units): self.units = units for s in self.getSpins(): if isinstance(s, SpinBox): s.setOpts(suffix=units) self.updateTitle() def getSpins(self): return [] #def setScale(self, scale): #self.scale = scale #self.updateTitle() def groupBoxClicked(self, b): self.setChildrenVisible(self.ui.groupBox, b) self.updateTitle() #if b: # self.ui.groupBox.setTitle(unicode(self.ui.groupBox.title())[4:]) #else: # self.ui.groupBox.setTitle("[+] " + unicode(self.ui.groupBox.title())) def setChildrenVisible(self, obj, vis): for c in obj.children(): if isinstance(c, QtGui.QWidget): c.setVisible(vis) else: self.setChildrenVisible(c, vis) def saveState(self): return self.stateGroup.state() def restoreState(self, state): self.stateGroup.setState(state) if hasattr(self.ui, 'waveGeneratorWidget'): self.ui.waveGeneratorWidget.update() def clearPlots(self): #for i in self.plots: #i.detach() #self.plots = [] self.plot.clear() self.currentPlot = None def displayCheckChanged(self): if self.stateGroup.state()['displayCheck']: self.plot.show() else: self.plot.hide() def taskStarted(self, params): pass def taskSequenceStarted(self): pass def quit(self): #print "quit DAQGeneric channel", self.name self.plot.close()
class PatchWindow(QtGui.QMainWindow): sigWindowClosed = QtCore.Signal(object) def __init__(self, dm, config): clampName = config['clampDev'] QtGui.QMainWindow.__init__(self) self.setWindowTitle(clampName) self.startTime = None self.redrawCommand = 1 self.analysisItems = { 'inputResistance': u'Ω', 'accessResistance': u'Ω', 'capacitance': 'F', 'restingPotential': 'V', 'restingCurrent': 'A', 'fitError': '' } self.params = { 'mode': 'vc', 'rate': config.get('sampleRate', 100000), 'downsample': config.get('downsample', 3), 'cycleTime': .2, 'recordTime': 0.1, 'delayTime': 0.03, 'pulseTime': 0.05, 'icPulse': -30e-12, 'vcPulse': -10e-3, 'icHolding': 0, 'vcHolding': -65e-3, 'icHoldingEnabled': False, 'icPulseEnabled': True, 'vcHoldingEnabled': False, 'vcPulseEnabled': True, 'drawFit': True, 'average': 1, } self.paramLock = Mutex(QtCore.QMutex.Recursive) self.manager = dm self.clampName = clampName self.thread = PatchThread(self) self.cw = QtGui.QWidget() self.setCentralWidget(self.cw) self.ui = Ui_Form() self.ui.setupUi(self.cw) #self.logBtn = LogButton("Log") #self.statusBar().addPermanentWidget(self.logBtn) self.setStatusBar(StatusBar()) self.stateFile = os.path.join('modules', self.clampName + '_ui.cfg') uiState = Manager.getManager().readConfigFile(self.stateFile) if 'geometry' in uiState: geom = QtCore.QRect(*uiState['geometry']) self.setGeometry(geom) if 'window' in uiState: ws = QtCore.QByteArray.fromPercentEncoding(uiState['window']) self.restoreState(ws) self.ui.splitter_2.setSizes([self.width() / 4, self.width() * 3. / 4.]) self.ui.splitter.setStretchFactor(0, 30) self.ui.splitter.setStretchFactor(1, 10) self.plots = {} for k in self.analysisItems: p = PlotWidget() p.setLabel('left', text=k, units=self.analysisItems[k]) self.ui.plotLayout.addWidget(p) self.plots[k] = p irp = self.plots['inputResistance'] irp.setLogMode(y=True, x=False) irp.setYRange(6, 11) self.ui.icPulseSpin.setOpts(dec=True, step=1, minStep=1e-12, bounds=[None, None], siPrefix=True, suffix='A') self.ui.vcPulseSpin.setOpts(dec=True, step=1, minStep=1e-3, bounds=[None, None], siPrefix=True, suffix='V') self.ui.icHoldSpin.setOpts(dec=True, step=1, minStep=1e-12, bounds=[None, None], siPrefix=True, suffix='A') self.ui.vcHoldSpin.setOpts(dec=True, step=1, minStep=1e-3, bounds=[None, None], siPrefix=True, suffix='V') self.ui.cycleTimeSpin.setOpts(dec=True, step=1, minStep=1e-6, bounds=[0, None], siPrefix=True, suffix='s') self.ui.pulseTimeSpin.setOpts(dec=True, step=1, minStep=1e-6, bounds=[0, 1.], siPrefix=True, suffix='s') self.ui.delayTimeSpin.setOpts(dec=True, step=1, minStep=1e-6, bounds=[0, 1.], siPrefix=True, suffix='s') self.stateGroup = WidgetGroup([ (self.ui.icPulseSpin, 'icPulse'), (self.ui.vcPulseSpin, 'vcPulse'), (self.ui.icHoldSpin, 'icHolding'), (self.ui.vcHoldSpin, 'vcHolding'), (self.ui.icPulseCheck, 'icPulseEnabled'), (self.ui.vcPulseCheck, 'vcPulseEnabled'), (self.ui.icHoldCheck, 'icHoldingEnabled'), (self.ui.vcHoldCheck, 'vcHoldingEnabled'), (self.ui.cycleTimeSpin, 'cycleTime'), (self.ui.pulseTimeSpin, 'pulseTime'), (self.ui.delayTimeSpin, 'delayTime'), (self.ui.drawFitCheck, 'drawFit'), (self.ui.averageSpin, 'average'), ]) self.stateGroup.setState(self.params) self.ui.patchPlot.setLabel('left', text='Primary', units='A') self.patchCurve = self.ui.patchPlot.plot( pen=QtGui.QPen(QtGui.QColor(200, 200, 200))) self.patchFitCurve = self.ui.patchPlot.plot( pen=QtGui.QPen(QtGui.QColor(0, 100, 200))) self.ui.commandPlot.setLabel('left', text='Command', units='V') self.commandCurve = self.ui.commandPlot.plot( pen=QtGui.QPen(QtGui.QColor(200, 200, 200))) self.ui.startBtn.clicked.connect(self.startClicked) self.ui.recordBtn.clicked.connect(self.recordClicked) self.ui.bathModeBtn.clicked.connect(self.bathMode) self.ui.patchModeBtn.clicked.connect(self.patchMode) self.ui.cellModeBtn.clicked.connect(self.cellMode) self.ui.monitorModeBtn.clicked.connect(self.monitorMode) self.ui.resetBtn.clicked.connect(self.resetClicked) self.thread.finished.connect(self.threadStopped) self.thread.sigNewFrame.connect(self.handleNewFrame) self.ui.vcModeRadio.toggled.connect(self.updateParams) self.stateGroup.sigChanged.connect(self.updateParams) ## Configure analysis plots, curves, and data arrays self.analysisCurves = {} self.analysisData = {'time': []} for n in self.analysisItems: w = getattr(self.ui, n + 'Check') w.clicked.connect(self.showPlots) p = self.plots[n] self.analysisCurves[n] = p.plot( pen=QtGui.QPen(QtGui.QColor(200, 200, 200))) for suf in ['', 'Std']: self.analysisData[n + suf] = [] self.showPlots() self.updateParams() self.show() self.bathMode() def quit(self): #print "Stopping patch thread.." geom = self.geometry() uiState = { 'window': str(self.saveState().toPercentEncoding()), 'geometry': [geom.x(), geom.y(), geom.width(), geom.height()] } Manager.getManager().writeConfigFile(uiState, self.stateFile) self.thread.stop(block=True) #print "Patch thread exited; module quitting." def closeEvent(self, ev): self.quit() self.sigWindowClosed.emit(self) def bathMode(self): self.ui.vcPulseCheck.setChecked(True) self.ui.vcHoldCheck.setChecked(False) self.ui.vcModeRadio.setChecked(True) self.ui.cycleTimeSpin.setValue(0.2) self.ui.pulseTimeSpin.setValue(10e-3) self.ui.delayTimeSpin.setValue(10e-3) self.ui.averageSpin.setValue(1) def patchMode(self): self.ui.vcPulseCheck.setChecked(True) self.ui.vcHoldCheck.setChecked(True) self.ui.vcModeRadio.setChecked(True) self.ui.cycleTimeSpin.setValue(0.2) self.ui.pulseTimeSpin.setValue(10e-3) self.ui.delayTimeSpin.setValue(10e-3) self.ui.averageSpin.setValue(1) def cellMode(self): self.ui.icPulseCheck.setChecked(True) self.ui.icModeRadio.setChecked(True) self.ui.cycleTimeSpin.setValue(250e-3) self.ui.pulseTimeSpin.setValue(150e-3) self.ui.delayTimeSpin.setValue(30e-3) self.ui.averageSpin.setValue(1) def monitorMode(self): self.ui.cycleTimeSpin.setValue(40) self.ui.averageSpin.setValue(5) def showPlots(self): """Show/hide analysis plot widgets""" for n in self.analysisItems: w = getattr(self.ui, n + 'Check') p = self.plots[n] if w.isChecked(): p.show() else: p.hide() self.updateAnalysisPlots() def updateParams(self, *args): with self.paramLock: if self.ui.icModeRadio.isChecked(): mode = 'ic' else: mode = 'vc' self.params['mode'] = mode state = self.stateGroup.state() for p in self.params: if p in state: self.params[p] = state[p] self.params['recordTime'] = self.params[ 'delayTime'] * 2.0 + self.params['pulseTime'] self.thread.updateParams() self.redrawCommand = 2 ## may need to redraw twice to make sure the update has gone through def recordClicked(self): if self.ui.recordBtn.isChecked(): data = self.makeAnalysisArray() if data.shape[0] == 0: ## no data yet; don't start the file self.storageFile = None return self.newFile(data) else: self.storageFile = None def newFile(self, data): sd = self.storageDir() self.storageFile = sd.writeFile(data, self.clampName, autoIncrement=True, appendAxis='Time', newFile=True) if self.startTime is not None: self.storageFile.setInfo({'startTime': self.startTime}) def storageDir(self): return self.manager.getCurrentDir().getDir('Patch', create=True) #def storageFile(self): #sd = self.storageDir() #return sd.getFile(self.clampName, create=True) def resetClicked(self): self.ui.recordBtn.setChecked(False) self.recordClicked() for n in self.analysisData: self.analysisData[n] = [] self.startTime = None def handleNewFrame(self, frame): prof = Profiler('PatchWindow.handleNewFrame', disabled=True) with self.paramLock: mode = self.params['mode'] data = frame['data'][self.clampName] if mode == 'vc': self.ui.patchPlot.setLabel('left', units='A') else: self.ui.patchPlot.setLabel('left', units='V') prof.mark('1') self.patchCurve.setData(data.xvals('Time'), data['primary']) prof.mark('2') if self.redrawCommand > 0: self.redrawCommand -= 1 #print "set command curve" self.commandCurve.setData(data.xvals('Time'), data['command']) if mode == 'vc': self.ui.commandPlot.setLabel('left', units='V') else: self.ui.commandPlot.setLabel('left', units='A') prof.mark('3') #self.ui.patchPlot.replot() #self.ui.commandPlot.replot() if frame['analysis']['fitTrace'] is not None: self.patchFitCurve.show() self.patchFitCurve.setData(data.xvals('Time'), frame['analysis']['fitTrace']) else: self.patchFitCurve.hide() prof.mark('4') for k in self.analysisItems: if k in frame['analysis']: self.analysisData[k].append(frame['analysis'][k]) prof.mark('5') for r in ['input', 'access']: res = r + 'Resistance' label = getattr(self.ui, res + 'Label') resistance = frame['analysis'][res] label.setText(siFormat(resistance) + u'Ω') prof.mark('6') self.ui.restingPotentialLabel.setText( siFormat(frame['analysis']['restingPotential'], error=frame['analysis']['restingPotentialStd'], suffix='V')) self.ui.restingCurrentLabel.setText( siFormat(frame['analysis']['restingCurrent'], error=frame['analysis']['restingCurrentStd'], suffix='A')) self.ui.capacitanceLabel.setText( '%sF' % siFormat(frame['analysis']['capacitance'])) self.ui.fitErrorLabel.setText('%7.2g' % frame['analysis']['fitError']) prof.mark('7') start = data._info[-1]['DAQ']['command']['startTime'] if self.startTime is None: self.startTime = start if self.ui.recordBtn.isChecked() and self.storageFile is not None: self.storageFile.setInfo({'startTime': self.startTime}) self.analysisData['time'].append(start - self.startTime) prof.mark('8') self.updateAnalysisPlots() prof.mark('9') ## Record to disk if requested. if self.ui.recordBtn.isChecked(): arr = self.makeAnalysisArray(lastOnly=True) #print "appending array", arr.shape if self.storageFile is None: self.newFile(arr) else: arr.write(self.storageFile.name(), appendAxis='Time') prof.mark('10') prof.finish() def makeAnalysisArray(self, lastOnly=False): ## Determine how much of the data to include in this array if lastOnly: sl = slice(-1, None) else: sl = slice(None) ## Generate the meta-info structure info = [{ 'name': 'Time', 'values': self.analysisData['time'][sl], 'units': 's' }, { 'name': 'Value', 'cols': [] }] for k in self.analysisItems: for s in ['', 'Std']: if len(self.analysisData[k + s]) < 1: continue info[1]['cols'].append({ 'name': k + s, 'units': self.analysisItems[k] }) ## Create the blank MetaArray data = MetaArray((len(info[0]['values']), len(info[1]['cols'])), dtype=float, info=info) ## Fill with data for k in self.analysisItems: for s in ['', 'Std']: if len(self.analysisData[k + s]) < 1: continue try: data[:, k + s] = self.analysisData[k + s][sl] except: print data.shape, data[:, k + s].shape, len( self.analysisData[k + s][sl]) raise return data def updateAnalysisPlots(self): for n in self.analysisItems: p = self.plots[n] if p.isVisible(): self.analysisCurves[n].setData(self.analysisData['time'], self.analysisData[n]) #if len(self.analysisData[n+'Std']) > 0: #self.analysisCurves[p+'Std'].setData(self.analysisData['time'], self.analysisData[n+'Std']) #p.replot() def startClicked(self): if self.ui.startBtn.isChecked(): if not self.thread.isRunning(): self.thread.start() Manager.logMsg("Patch module started.") self.ui.startBtn.setText('Stop') else: self.ui.startBtn.setEnabled(False) self.thread.stop() Manager.logMsg("Patch module stopped.") def threadStopped(self): self.ui.startBtn.setText('Start') self.ui.startBtn.setEnabled(True) self.ui.startBtn.setChecked(False)