class AnalysisModule(QtGui.QWidget): def __init__(self, taskRunner): QtGui.QWidget.__init__(self) self.pr = taskRunner self.pr.sigNewFrame.connect(self.newFrame) self.pr.sigTaskSequenceStarted.connect(self.taskSequenceStarted) self.pr.sigTaskStarted.connect(self.taskStarted) self.pr.sigTaskFinished.connect(self.taskFinished) def postGuiInit(self): self.stateGroup = WidgetGroup(self) def newFrame(self, *args): pass #print "NEW FRAME!" #print args def taskStarted(self, *args): pass #print "taskStarted!" def taskFinished(self): pass def taskSequenceStarted(self, *args): pass #print "taskStarted!" def saveState(self): return self.stateGroup.state() def restoreState(self, state): self.stateGroup.setState(state) def quit(self): try: self.pr.sigNewFrame.disconnect(self.newFrame) except TypeError: pass try: self.pr.sigTaskSequenceStarted.disconnect(self.taskSequenceStarted) except TypeError: pass try: self.pr.sigTaskStarted.disconnect(self.taskStarted) except TypeError: pass try: self.pr.sigTaskFinished.disconnect(self.taskFinished) except TypeError: pass
class DAQGenericTaskGui(TaskGui): #sigSequenceChanged = QtCore.Signal(object) ## defined upstream def __init__(self, dev, task, ownUi=True): TaskGui.__init__(self, dev, task) self.plots = weakref.WeakValueDictionary() self.channels = {} if ownUi: self.ui = Ui_Form() self.ui.setupUi(self) self.stateGroup = WidgetGroup([ (self.ui.topSplitter, 'splitter1'), (self.ui.controlSplitter, 'splitter2'), (self.ui.plotSplitter, 'splitter3'), ]) self.createChannelWidgets(self.ui.controlSplitter, self.ui.plotSplitter) self.ui.topSplitter.setStretchFactor(0, 0) self.ui.topSplitter.setStretchFactor(1, 1) else: ## If ownUi is False, then the UI is created elsewhere and createChannelWidgets must be called from there too. self.stateGroup = None def createChannelWidgets(self, ctrlParent, plotParent): ## Create plots and control widgets for ch in self.dev._DGConfig: (w, p) = self.createChannelWidget(ch) plotParent.addWidget(p) ctrlParent.addWidget(w) def createChannelWidget(self, ch, daqName=None): conf = self.dev._DGConfig[ch] p = PlotWidget(self) units = '' if 'units' in conf: units = conf['units'] #p.setAxisTitle(PlotWidget.yLeft, ch+units) p.setLabel('left', text=ch, units=units) #print "Plot label:", ch, units self.plots[ch] = p p.registerPlot(self.dev.name() + '.' + ch) if conf['type'] in ['ao', 'do']: w = OutputChannelGui(self, ch, conf, p, self.dev, self.taskRunner, daqName) #QtCore.QObject.connect(w, QtCore.SIGNAL('sequenceChanged'), self.sequenceChanged) w.sigSequenceChanged.connect(self.sequenceChanged) elif conf['type'] in ['ai', 'di']: w = InputChannelGui(self, ch, conf, p, self.dev, self.taskRunner, daqName) else: raise Exception("Unrecognized device type '%s'" % conf['type']) # w.setUnits(units) self.channels[ch] = w return (w, p) def saveState(self): if self.stateGroup is not None: state = self.stateGroup.state().copy() else: state = {} state['channels'] = {} for ch in self.channels: state['channels'][ch] = self.channels[ch].saveState() return state def restoreState(self, state): try: if self.stateGroup is not None: self.stateGroup.setState(state) for ch in state['channels']: try: self.channels[ch].restoreState(state['channels'][ch]) except KeyError: printExc("Warning: Cannot restore state for channel %s.%s (channel does not exist on this device)" % (self.dev.name(), ch)) continue self.channels[ch].restoreState(state['channels'][ch]) except: printExc('Error while restoring GUI state:') #sys.excepthook(*sys.exc_info()) #self.ui.waveGeneratorWidget.update() def listSequence(self): ## returns sequence parameter names and lengths l = {} for ch in self.channels: chl = self.channels[ch].listSequence() for k in chl: l[ch+'.'+k] = chl[k] return l def sequenceChanged(self): #self.emit(QtCore.SIGNAL('sequenceChanged'), self.dev.name()) self.sigSequenceChanged.emit(self.dev.name()) def taskStarted(self, params): ## automatically invoked from TaskGui ## Pull out parameters for this device params = dict([(p[1], params[p]) for p in params if p[0] == self.dev.name()]) for ch in self.channels: ## Extract just the parameters the channel will need chParams = {} search = ch + '.' for k in params: if k[:len(search)] == search: chParams[k[len(search):]] = params[k] self.channels[ch].taskStarted(chParams) def taskSequenceStarted(self): ## automatically invoked from TaskGui for ch in self.channels: self.channels[ch].taskSequenceStarted() def generateTask(self, params=None): #print "generating task with:", params if params is None: params = {} p = {} for ch in self.channels: ## Extract just the parameters the channel will need chParams = {} search = ch + '.' for k in params: if k[:len(search)] == search: chParams[k[len(search):]] = params[k] ## request the task from the channel #print " requesting %s task with params:"%ch, chParams p[ch] = self.channels[ch].generateTask(chParams) #print p return p def handleResult(self, result, params): #print "handling result:", result if result is None: return for ch in self.channels: #print "\nhandle result for", ch #print 'result:', type(result) #print result #print "--------\n" #if ch not in result: #print " no result" #continue #print result.infoCopy() if result.hasColumn(0, ch): self.channels[ch].handleResult(result[ch], params) def getChanHolding(self, chan): """Return the holding value that this channel will use when the task is run.""" return self.dev.getChanHolding(chan) def quit(self): TaskGui.quit(self) for ch in self.channels: self.channels[ch].quit()
class NiDAQTask(TaskGui): sigChanged = QtCore.Signal(object) def __init__(self, dev, task): TaskGui.__init__(self, dev, task) self.ui = Ui_Form() self.ui.setupUi(self) self.nPts = 0 self.ignorePeriod = False self.ignoreRate = False self.rate = 40e3 self.updateNPts() self.updateDevList() #self.devs = [] self.ui.rateSpin.setOpts(dec=True, step=0.5, minStep=10, bounds=[1,None], siPrefix=True, suffix='Hz') self.ui.periodSpin.setOpts(dec=True, step=0.5, minStep=1e-6, bounds=[1e-6,None], siPrefix=True, suffix='s') self.ui.besselCutoffSpin.setOpts(value=20e3, dec=True, step=0.5, minStep=10, bounds=[1,None], siPrefix=True, suffix='Hz') self.ui.butterworthPassbandSpin.setOpts(value=20e3, dec=True, step=0.5, minStep=10, bounds=[1,None], siPrefix=True, suffix='Hz') self.ui.butterworthStopbandSpin.setOpts(value=40e3, dec=True, step=0.5, minStep=10, bounds=[1,None], siPrefix=True, suffix='Hz') ## important to create widget group before connecting anything else. self.stateGroup = WidgetGroup([ (self.ui.rateSpin, 'rate'), (self.ui.downsampleSpin, 'downsample'), (self.ui.triggerDevList, 'triggerDevice'), (self.ui.denoiseCombo, 'denoiseMethod'), (self.ui.denoiseThresholdSpin, 'denoiseThreshold'), (self.ui.denoiseWidthSpin, 'denoiseWidth'), (self.ui.filterCombo, 'filterMethod'), (self.ui.besselCutoffSpin, 'besselCutoff'), (self.ui.besselOrderSpin, 'besselOrder'), (self.ui.butterworthPassbandSpin, 'butterworthPassband'), (self.ui.butterworthStopbandSpin, 'butterworthStopband'), (self.ui.butterworthPassDBSpin, 'butterworthPassDB'), (self.ui.butterworthStopDBSpin, 'butterworthStopDB'), ]) self.ui.rateSpin.valueChanged.connect(self.rateChanged) self.ui.periodSpin.sigValueChanging.connect(self.updateRateSpin) self.ui.rateSpin.sigValueChanging.connect(self.updatePeriodSpin) self.task.sigTaskChanged.connect(self.taskChanged) self.ui.denoiseCombo.currentIndexChanged.connect(self.ui.denoiseStack.setCurrentIndex) self.ui.filterCombo.currentIndexChanged.connect(self.ui.filterStack.setCurrentIndex) self.ui.rateSpin.setValue(self.rate) def quit(self): TaskGui.quit(self) QtCore.QObject.disconnect(self.task, QtCore.SIGNAL('taskChanged'), self.taskChanged) def saveState(self): return self.stateGroup.state() def restoreState(self, state): try: #if 'rate' in state: #if 'downsample' in state: #self.ui.downsampleSpin.setValue(state['downsample']) #self.ui.rateSpin.setValue(state['rate'] / 1000.) ##print "trigger dev:", state['triggerDevice'] ##print self.devs #if 'triggerDevice' in state and state['triggerDevice'] in self.devs: #self.ui.triggerDevList.setCurrentIndex(self.devs.index(state['triggerDevice'])+1) ##print "Set index to", self.devs.index(state['triggerDevice'])+1 #else: #self.ui.triggerDevList.setCurrentIndex(0) ##print "No index" #else: self.stateGroup.setState(state) except: #sys.excepthook(*sys.exc_info()) printExc("Error while loading DAQ task GUI configuration (proceeding with default configuration) :") def generateTask(self, params=None): task = self.currentState() task2 = {} # just for cleanliness, remove any filtering parameters that are not in use: remNames = ['butterworth', 'bessel'] if task['filterMethod'].lower() in remNames: remNames.remove(task['filterMethod'].lower()) if task['denoiseMethod'] == 'None': remNames.append('denoiseWidth') remNames.append('denoiseThreshold') for k in task: if not any(map(k.startswith, remNames)): task2[k] = task[k] return task2 def currentState(self): self.updateNPts() state = self.stateGroup.state() ## make sure all of these are up to date: state['numPts'] = self.nPts state['rate'] = self.rate state['downsample'] = self.ui.downsampleSpin.value() if self.ui.triggerDevList.currentIndex() > 0: state['triggerDevice'] = str(self.ui.triggerDevList.currentText()) else: del state['triggerDevice'] return state def updatePeriodSpin(self): if self.ignoreRate: return period = 1. / self.ui.rateSpin.value() self.ignorePeriod = True self.ui.periodSpin.setValue(period) self.ignorePeriod = False def updateRateSpin(self): if self.ignorePeriod: return period = self.ui.periodSpin.value() rate = 1.0 / period self.ignoreRate = True self.ui.rateSpin.setValue(rate) self.ignoreRate = False def rateChanged(self): self.rate = self.ui.rateSpin.value() self.updateNPts() self.sigChanged.emit(self.currentState()) def taskChanged(self, n, v): #print "caught task change", n, v if n == 'duration': self.updateNPts() self.sigChanged.emit(self.currentState()) def updateNPts(self): dur = self.task.getParam('duration') nPts = int(dur * self.rate) if nPts != self.nPts: self.nPts = nPts self.ui.numPtsLabel.setText(str(self.nPts)) def updateDevList(self): ## list all devices allDevNames = self.dev.dm.listDevices() ## convert device names into device handles allDevs = [self.dev.dm.getDevice(d) for d in allDevNames] ## select out devices which have trigger channel to this DAQ self.devs = [d.name() for d in allDevs if d.getTriggerChannel(self.dev.name()) is not None] self.ui.triggerDevList.clear() self.ui.triggerDevList.addItem('No Trigger') for d in self.devs: #print d, self.dev.name #dev = self.dev.dm.getDevice(d) #if dev.getTriggerChannel(self.dev.name) is not None: #print "------" self.ui.triggerDevList.addItem(d)
class NiDAQTask(TaskGui): sigChanged = QtCore.Signal(object) def __init__(self, dev, taskRunner): TaskGui.__init__(self, dev, taskRunner) self.ui = Ui_Form() self.ui.setupUi(self) self.nPts = 0 self.ignorePeriod = False self.ignoreRate = False self.rate = 40e3 self._block_update = False # use to block signal emission during bulk updates self.updateNPts() self.updateDevList() self.ui.rateSpin.setOpts(dec=True, step=0.5, minStep=10, bounds=[1, None], siPrefix=True, suffix='Hz') self.ui.periodSpin.setOpts(dec=True, step=0.5, minStep=1e-6, bounds=[1e-6, None], siPrefix=True, suffix='s') self.ui.besselCutoffSpin.setOpts(value=20e3, dec=True, step=0.5, minStep=10, bounds=[1, None], siPrefix=True, suffix='Hz') self.ui.butterworthPassbandSpin.setOpts(value=20e3, dec=True, step=0.5, minStep=10, bounds=[1, None], siPrefix=True, suffix='Hz') self.ui.butterworthStopbandSpin.setOpts(value=40e3, dec=True, step=0.5, minStep=10, bounds=[1, None], siPrefix=True, suffix='Hz') ## important to create widget group before connecting anything else. self.stateGroup = WidgetGroup([ (self.ui.rateSpin, 'rate'), (self.ui.downsampleSpin, 'downsample'), (self.ui.triggerDevList, 'triggerDevice'), (self.ui.denoiseCombo, 'denoiseMethod'), (self.ui.denoiseThresholdSpin, 'denoiseThreshold'), (self.ui.denoiseWidthSpin, 'denoiseWidth'), (self.ui.filterCombo, 'filterMethod'), (self.ui.besselCutoffSpin, 'besselCutoff'), (self.ui.besselOrderSpin, 'besselOrder'), (self.ui.besselBidirCheck, 'besselBidirectional'), (self.ui.butterworthPassbandSpin, 'butterworthPassband'), (self.ui.butterworthStopbandSpin, 'butterworthStopband'), (self.ui.butterworthPassDBSpin, 'butterworthPassDB'), (self.ui.butterworthStopDBSpin, 'butterworthStopDB'), (self.ui.butterworthBidirCheck, 'butterworthBidirectional'), ]) self.ui.periodSpin.sigValueChanging.connect(self.updateRateSpin) self.ui.rateSpin.sigValueChanging.connect(self.updatePeriodSpin) self.taskRunner.sigTaskChanged.connect(self.taskChanged) self.ui.denoiseCombo.currentIndexChanged.connect( self.updateDenoiseCtrl) self.ui.filterCombo.currentIndexChanged.connect(self.updateFilterCtrl) self.ui.rateSpin.setValue(self.rate) self.stateGroup.sigChanged.connect(self.stateChanged) self.updateDenoiseCtrl() self.updateFilterCtrl() def quit(self): self.taskRunner.sigTaskChanged.disconnect(self.taskChanged) TaskGui.quit(self) def saveState(self): return self.stateGroup.state() def restoreState(self, state): block = self._block_update try: self._block_update = True self.stateGroup.setState(state) except Exception: printExc( "Error while loading DAQ task GUI configuration (proceeding with default configuration) :" ) finally: self._block_update = block self.stateChanged() def generateTask(self, params=None): task = self.currentState() task2 = {} # just for cleanliness, remove any filtering parameters that are not in use: remNames = ['butterworth', 'bessel'] if task['filterMethod'].lower() in remNames: remNames.remove(task['filterMethod'].lower()) if task['denoiseMethod'] == 'None': remNames.append('denoiseWidth') remNames.append('denoiseThreshold') for k in task: if not any(map(k.startswith, remNames)): task2[k] = task[k] return task2 def currentState(self): self.updateNPts() state = self.stateGroup.state() ## make sure all of these are up to date: state['numPts'] = self.nPts state['rate'] = self.rate state['downsample'] = self.ui.downsampleSpin.value() if self.ui.triggerDevList.currentIndex() > 0: state['triggerDevice'] = str(self.ui.triggerDevList.currentText()) else: del state['triggerDevice'] return state def updatePeriodSpin(self): if self.ignoreRate: return period = 1. / self.ui.rateSpin.value() self.ignorePeriod = True self.ui.periodSpin.setValue(period) self.ignorePeriod = False def updateRateSpin(self): if self.ignorePeriod: return period = self.ui.periodSpin.value() rate = 1.0 / period self.ignoreRate = True self.ui.rateSpin.setValue(rate) self.ignoreRate = False def stateChanged(self, name=None, val=None): if name == 'rate': self.rate = self.ui.rateSpin.value() self.updateNPts() if self._block_update: return state = self.currentState() self.sigChanged.emit(state) def taskChanged(self, n, v): if n == 'duration': self.updateNPts() self.sigChanged.emit(self.currentState()) def updateNPts(self): dur = self.taskRunner.getParam('duration') nPts = int(dur * self.rate) if nPts != self.nPts: self.nPts = nPts self.ui.numPtsLabel.setText(str(self.nPts)) def updateDevList(self): ## list all devices allDevNames = self.dev.dm.listDevices() ## convert device names into device handles allDevs = [self.dev.dm.getDevice(d) for d in allDevNames] ## select out devices which have trigger channel to this DAQ self.devs = [ d.name() for d in allDevs if d.getTriggerChannel(self.dev.name()) is not None ] self.ui.triggerDevList.clear() self.ui.triggerDevList.addItem('No Trigger') for d in self.devs: self.ui.triggerDevList.addItem(d) def updateDenoiseCtrl(self): denoise = self.ui.denoiseCombo.currentText() if denoise == 'None': self.ui.denoiseCtrl.hide() else: self.ui.denoiseCtrl.show() def updateFilterCtrl(self): filter = self.ui.filterCombo.currentText() if filter == 'None': self.ui.besselCtrl.hide() self.ui.butterworthCtrl.hide() elif filter == 'Bessel': self.ui.besselCtrl.show() self.ui.butterworthCtrl.hide() elif filter == 'Butterworth': self.ui.besselCtrl.hide() self.ui.butterworthCtrl.show()
class MultiClampTaskGui(TaskGui): #sigSequenceChanged = Qt.Signal(object) ## defined upstream def __init__(self, dev, taskRunner): TaskGui.__init__(self, dev, taskRunner) daqDev = self.dev.getDAQName() self.daqUI = self.taskRunner.getDevice(daqDev) self.traces = { } ## Stores traces from a sequence to allow average plotting self.resetInpPlots = False ## Signals result handler to clear plots before adding a new one self.currentCmdPlot = None self._block_update = False # blocks plotting during state changes self.ui = Ui_Form() self.ui.setupUi(self) self.ui.splitter_2.setStretchFactor(0, 0) self.ui.splitter_2.setStretchFactor(1, 1) self.ui.splitter.setStretchFactor(0, 3) self.ui.splitter.setStretchFactor(1, 1) self.stateGroup = WidgetGroup(self) #self.ui.waveGeneratorWidget.setTimeScale(1e-3) self.ui.waveGeneratorWidget.setMeta('x', units='s', siPrefix=True, dec=True, step=0.5, minStep=1e-6) self.unitLabels = [self.ui.waveGeneratorLabel, self.ui.holdingCheck] #self.modeSignalList = self.dev.listModeSignals() self.mode = None self.setMode('I=0') self.ui.topPlotWidget.registerPlot(self.dev.name() + '.Input') self.ui.topPlotWidget.setDownsampling(ds=True, auto=True, mode='peak') self.ui.topPlotWidget.setClipToView(True) self.ui.bottomPlotWidget.registerPlot(self.dev.name() + '.Command') self.ui.bottomPlotWidget.setDownsampling(ds=True, auto=True, mode='peak') self.ui.bottomPlotWidget.setClipToView(True) self.daqChanged(self.daqUI.currentState()) self.daqUI.sigChanged.connect(self.daqChanged) self.ui.waveGeneratorWidget.sigDataChanged.connect(self.updateWaves) self.ui.waveGeneratorWidget.sigParametersChanged.connect( self.sequenceChanged) self.stateGroup.sigChanged.connect(self.uiStateChanged) self.dev.sigStateChanged.connect(self.devStateChanged) self.dev.sigHoldingChanged.connect(self.devHoldingChanged) self.uiStateChanged('', '') self.devStateChanged() def uiStateChanged(self, name, value): if 'ModeRadio' in name: self.setMode() if self.getMode() == 'I=0': self.ui.holdingCheck.setChecked(False) self.ui.holdingCheck.setEnabled(False) else: self.ui.holdingCheck.setEnabled(True) checkMap = { 'holdingCheck': self.ui.holdingSpin, 'primarySignalCheck': self.ui.primarySignalCombo, 'secondarySignalCheck': self.ui.secondarySignalCombo, 'primaryGainCheck': self.ui.primaryGainSpin, 'secondaryGainCheck': self.ui.secondaryGainSpin, } ## For each check box, enable its corresponding control if name in checkMap: checkMap[name].setEnabled(value) self.devStateChanged() def devStateChanged(self, state=None): mode = self.getMode() state = self.dev.getLastState(mode) if not self.ui.holdingSpin.isEnabled(): self.ui.holdingSpin.setValue(state['holding']) if not self.ui.primaryGainSpin.isEnabled(): self.ui.primaryGainSpin.setValue(state['primaryGain']) if not self.ui.secondaryGainSpin.isEnabled(): self.ui.secondaryGainSpin.setValue(state['secondaryGain']) psig = ssig = None if not self.ui.primarySignalCombo.isEnabled(): psig = state['primarySignal'] if not self.ui.secondarySignalCombo.isEnabled(): ssig = state['secondarySignal'] self.setSignals(psig, ssig) def devHoldingChanged(self, dev, mode): if mode != self.getMode(): return if not self.ui.holdingSpin.isEnabled(): state = self.dev.getLastState(mode) self.ui.holdingSpin.setValue(state['holding']) def saveState(self): state = self.stateGroup.state().copy() state['mode'] = self.getMode() state['primarySignal'] = str(self.ui.primarySignalCombo.currentText()) state['secondarySignal'] = str( self.ui.secondarySignalCombo.currentText()) return state def restoreState(self, state): block = self._block_update try: self._block_update = True self.setMode(state['mode']) if 'primarySignal' in state and 'secondarySignal' in state: self.setSignals(state['primarySignal'], state['secondarySignal']) self.stateGroup.setState(state) self.devStateChanged() except: printExc('Error while restoring MultiClamp task GUI state:') finally: self._block_update = block self.updateWaves() def daqChanged(self, state): self.rate = state['rate'] self.numPts = state['numPts'] self.timeVals = numpy.linspace(0, float(self.numPts) / self.rate, self.numPts) self.updateWaves() def listSequence(self): return self.ui.waveGeneratorWidget.listSequences() def sequenceChanged(self): self.sigSequenceChanged.emit(self.dev.name()) def updateWaves(self): if self._block_update: return self.clearCmdPlots() ## compute sequence waves params = {} ps = self.ui.waveGeneratorWidget.listSequences() for k in ps: params[k] = range(len(ps[k])) waves = [] runSequence(lambda p: waves.append(self.getSingleWave(p)), params, list(params.keys())) # Plot all waves but disable auto-range first to improve performance. autoRange = self.ui.bottomPlotWidget.getViewBox().autoRangeEnabled() self.ui.bottomPlotWidget.enableAutoRange(x=False, y=False) try: for w in waves: if w is not None: self.plotCmdWave(w, color=Qt.QColor(100, 100, 100), replot=False) ## display single-mode wave in red single = self.getSingleWave() if single is not None: p = self.plotCmdWave(single, color=Qt.QColor(200, 100, 100)) p.setZValue(1000) finally: # re-enable auto range if needed self.ui.bottomPlotWidget.enableAutoRange(x=autoRange[0], y=autoRange[1]) def clearCmdPlots(self): self.ui.bottomPlotWidget.clear() self.currentCmdPlot = None def taskSequenceStarted(self): self.resetInpPlots = True def clearInpPlots(self): self.traces = {} self.ui.topPlotWidget.clear() def taskStarted(self, params): ## Draw green trace for current command waveform if self.currentCmdPlot is not None: self.ui.bottomPlotWidget.removeItem(self.currentCmdPlot) params = dict([(p[1], params[p]) for p in params if p[0] == self.dev.name()]) cur = self.getSingleWave(params) if cur is not None: self.currentCmdPlot = self.plotCmdWave(cur, color=Qt.QColor( 100, 200, 100)) self.currentCmdPlot.setZValue(1001) def plotCmdWave(self, data, color=Qt.QColor(100, 100, 100), replot=True): if data is None: return plot = self.ui.bottomPlotWidget.plot(data, x=self.timeVals) plot.setPen(Qt.QPen(color)) return plot def generateTask(self, params=None): state = self.stateGroup.state() if params is None: params = {} task = {} mode = self.getMode() task['mode'] = mode task['recordState'] = True #if self.ui.primarySignalCheck.isChecked(): #task['primary'] = self.ui.primarySignalCombo.currentText() #if self.ui.secondarySignalCheck.isChecked(): #task['secondary'] = self.ui.secondarySignalCombo.currentText() if state['primarySignalCheck']: task['primarySignal'] = state['primarySignalCombo'] if state['secondarySignalCheck']: task['secondarySignal'] = state['secondarySignalCombo'] if state['primaryGainCheck']: task['primaryGain'] = state['primaryGainSpin'] if state['secondaryGainCheck']: task['secondaryGain'] = state['secondaryGainSpin'] if mode != 'I=0': ## Must scale command to V or A before sending to task system. wave = self.getSingleWave(params) if wave is not None: task['command'] = wave if state['holdingCheck']: task['holding'] = state['holdingSpin'] #print "Task:", task return task def getSingleWave(self, params=None): state = self.stateGroup.state() h = state['holdingSpin'] self.ui.waveGeneratorWidget.setOffset(h) ## waveGenerator generates values in V or A wave = self.ui.waveGeneratorWidget.getSingle(self.rate, self.numPts, params) if wave is None: return None return wave def getMode(self): if self.ui.icModeRadio.isChecked(): self.mode = 'IC' elif self.ui.i0ModeRadio.isChecked(): self.mode = 'I=0' else: self.mode = 'VC' return self.mode def setMode(self, mode=None): if mode != self.mode: oldMode = self.mode if mode is None: mode = self.getMode() #print "Set mode to", mode # set radio button if mode == 'IC': self.ui.icModeRadio.setChecked(True) elif mode == 'I=0': self.ui.i0ModeRadio.setChecked(True) else: self.ui.vcModeRadio.setChecked(True) # update signal lists self.stateGroup.blockSignals(True) sigs = self.dev.listSignals(mode) #print "Signals:", sigs #print "-------" for s, c in [(sigs[0], self.ui.primarySignalCombo), (sigs[1], self.ui.secondarySignalCombo)]: c.clear() for ss in s: c.addItem(ss) self.stateGroup.blockSignals(False) # Disable signal, holding, and gain checks (only when switching between v and i modes) if mode == 'VC' or oldMode == 'VC': self.ui.primarySignalCheck.setChecked(False) self.ui.secondarySignalCheck.setChecked(False) self.ui.holdingCheck.setChecked(False) self.ui.primaryGainCheck.setChecked(False) self.ui.secondaryGainCheck.setChecked(False) self.devStateChanged() # update unit labels and scaling if mode == 'VC': newUnit = 'V' oldUnit = 'A' spinOpts = dict(suffix='V', siPrefix=True, dec=True, step=0.5, minStep=1e-3) self.ui.waveGeneratorWidget.setMeta('y', **spinOpts) self.ui.waveGeneratorWidget.setMeta('xy', units='V*s', siPrefix=True, dec=True, step=0.5, minStep=1e-6) else: newUnit = 'A' oldUnit = 'V' spinOpts = dict(suffix='A', siPrefix=True, dec=True, step=0.5, minStep=1e-12) self.ui.waveGeneratorWidget.setMeta('y', **spinOpts) self.ui.waveGeneratorWidget.setMeta('xy', units='C', siPrefix=True, dec=True, step=0.5, minStep=1e-15) self.ui.holdingSpin.setOpts(**spinOpts) for l in self.unitLabels: text = str(l.text()) l.setText(text.replace(oldUnit, newUnit)) self.ui.topPlotWidget.setLabel('left', units=oldUnit) self.ui.bottomPlotWidget.setLabel('left', units=newUnit) ## Hide stim plot for I=0 mode if mode == 'I=0': self.ui.bottomPlotWidget.hide() else: self.ui.bottomPlotWidget.show() self.devStateChanged() self.mode = mode def setSignals(self, pri, sec): #print "setSignals", pri, sec for c, s in [(self.ui.primarySignalCombo, pri), (self.ui.secondarySignalCombo, sec)]: if s is None: continue ind = c.findText(s) if ind == -1: for i in range(c.count()): print(c.itemText(i)) raise Exception('Signal "%s" does not exist' % s) c.setCurrentIndex(ind) def handleResult(self, result, params): if self.resetInpPlots: self.resetInpPlots = False self.clearInpPlots() ## Plot the results #plot = self.ui.topPlotWidget.plot(result['primary'].view(numpy.ndarray) / self.inpScale, x=result.xvals('Time'), params=params) plot = self.ui.topPlotWidget.plot(result['primary'].view( numpy.ndarray), x=result.xvals('Time'), params=params) def quit(self): TaskGui.quit(self) if not sip.isdeleted(self.daqUI): Qt.QObject.disconnect(self.daqUI, Qt.SIGNAL('changed'), self.daqChanged) self.ui.topPlotWidget.close() self.ui.bottomPlotWidget.close()
class MultiClampTaskGui(TaskGui): #sigSequenceChanged = QtCore.Signal(object) ## defined upstream def __init__(self, dev, task): TaskGui.__init__(self, dev, task) daqDev = self.dev.getDAQName() self.daqUI = self.task.getDevice(daqDev) self.traces = {} ## Stores traces from a sequence to allow average plotting self.resetInpPlots = False ## Signals result handler to clear plots before adding a new one self.currentCmdPlot = None self.ui = Ui_Form() self.ui.setupUi(self) self.ui.splitter_2.setStretchFactor(0, 0) self.ui.splitter_2.setStretchFactor(1, 1) self.ui.splitter.setStretchFactor(0, 3) self.ui.splitter.setStretchFactor(1, 1) self.stateGroup = WidgetGroup(self) #self.ui.waveGeneratorWidget.setTimeScale(1e-3) self.ui.waveGeneratorWidget.setMeta('x', units='s', siPrefix=True, dec=True, step=0.5, minStep=1e-6) self.unitLabels = [self.ui.waveGeneratorLabel, self.ui.holdingCheck] #self.modeSignalList = self.dev.listModeSignals() self.mode = None self.setMode('I=0') self.ui.topPlotWidget.registerPlot(self.dev.name() + '.Input') self.ui.bottomPlotWidget.registerPlot(self.dev.name() + '.Command') self.daqChanged(self.daqUI.currentState()) self.daqUI.sigChanged.connect(self.daqChanged) self.ui.waveGeneratorWidget.sigDataChanged.connect(self.updateWaves) self.ui.waveGeneratorWidget.sigParametersChanged.connect(self.sequenceChanged) self.stateGroup.sigChanged.connect(self.uiStateChanged) self.dev.sigStateChanged.connect(self.devStateChanged) self.devStateChanged() def uiStateChanged(self, name, value): if 'ModeRadio' in name: self.setMode() #i0Checks = [self.ui.holdingCheck, self.ui.primaryGainCheck, self.ui.secondaryGainCheck] if self.getMode() == 'I=0': self.ui.holdingCheck.setChecked(False) self.ui.holdingCheck.setEnabled(False) #for c in i0Checks: #c.setChecked(False) #c.setEnabled(False) else: self.ui.holdingCheck.setEnabled(True) #for c in i0Checks: #c.setEnabled(True) checkMap = { 'holdingCheck': self.ui.holdingSpin, 'primarySignalCheck': self.ui.primarySignalCombo, 'secondarySignalCheck': self.ui.secondarySignalCombo, 'primaryGainCheck': self.ui.primaryGainSpin, 'secondaryGainCheck': self.ui.secondaryGainSpin, } ## For each check box, enable its corresponding control if name in checkMap: checkMap[name].setEnabled(value) self.devStateChanged() def devStateChanged(self, state=None): mode = self.getMode() state = self.dev.getLastState(mode) if not self.ui.holdingSpin.isEnabled(): self.ui.holdingSpin.setValue(state['holding']) if not self.ui.primaryGainSpin.isEnabled(): self.ui.primaryGainSpin.setValue(state['primaryGain']) if not self.ui.secondaryGainSpin.isEnabled(): self.ui.secondaryGainSpin.setValue(state['secondaryGain']) psig = ssig = None if not self.ui.primarySignalCombo.isEnabled(): psig = state['primarySignal'] if not self.ui.secondarySignalCombo.isEnabled(): ssig = state['secondarySignal'] self.setSignals(psig, ssig) def saveState(self): state = self.stateGroup.state().copy() state['mode'] = self.getMode() state['primarySignal'] = str(self.ui.primarySignalCombo.currentText()) state['secondarySignal'] = str(self.ui.secondarySignalCombo.currentText()) return state def restoreState(self, state): try: self.setMode(state['mode']) if 'primarySignal' in state and 'secondarySignal' in state: self.setSignals(state['primarySignal'], state['secondarySignal']) self.stateGroup.setState(state) except: printExc('Error while restoring MultiClamp task GUI state:') #self.ui.waveGeneratorWidget.update() ## should be called as a result of stateGroup.setState; don't need to call again def daqChanged(self, state): self.rate = state['rate'] self.numPts = state['numPts'] self.timeVals = numpy.linspace(0, float(self.numPts)/self.rate, self.numPts) self.updateWaves() def listSequence(self): return self.ui.waveGeneratorWidget.listSequences() def sequenceChanged(self): #self.emit(QtCore.SIGNAL('sequenceChanged'), self.dev.name) self.sigSequenceChanged.emit(self.dev.name()) def updateWaves(self): self.clearCmdPlots() ## display sequence waves params = {} ps = self.ui.waveGeneratorWidget.listSequences() for k in ps: params[k] = range(len(ps[k])) waves = [] runSequence(lambda p: waves.append(self.getSingleWave(p)), params, params.keys()) for w in waves: if w is not None: #self.plotCmdWave(w / self.cmdScale, color=QtGui.QColor(100, 100, 100), replot=False) self.plotCmdWave(w, color=QtGui.QColor(100, 100, 100), replot=False) ## display single-mode wave in red single = self.getSingleWave() if single is not None: #self.plotCmdWave(single / self.cmdScale, color=QtGui.QColor(200, 100, 100)) p = self.plotCmdWave(single, color=QtGui.QColor(200, 100, 100)) p.setZValue(1000) #self.paramListChanged def clearCmdPlots(self): self.ui.bottomPlotWidget.clear() self.currentCmdPlot = None def taskSequenceStarted(self): self.resetInpPlots = True def clearInpPlots(self): self.traces = {} self.ui.topPlotWidget.clear() def taskStarted(self, params): ## Draw green trace for current command waveform if self.currentCmdPlot is not None: self.ui.bottomPlotWidget.removeItem(self.currentCmdPlot) params = dict([(p[1], params[p]) for p in params if p[0] == self.dev.name()]) cur = self.getSingleWave(params) if cur is not None: self.currentCmdPlot = self.plotCmdWave(cur, color=QtGui.QColor(100, 200, 100)) self.currentCmdPlot.setZValue(1001) def plotCmdWave(self, data, color=QtGui.QColor(100, 100, 100), replot=True): if data is None: return plot = self.ui.bottomPlotWidget.plot(data, x=self.timeVals) plot.setPen(QtGui.QPen(color)) return plot def generateTask(self, params=None): state = self.stateGroup.state() if params is None: params = {} task = {} mode = self.getMode() task['mode'] = mode task['recordState'] = True #if self.ui.primarySignalCheck.isChecked(): #task['primary'] = self.ui.primarySignalCombo.currentText() #if self.ui.secondarySignalCheck.isChecked(): #task['secondary'] = self.ui.secondarySignalCombo.currentText() if state['primarySignalCheck']: task['primarySignal'] = state['primarySignalCombo'] if state['secondarySignalCheck']: task['secondarySignal'] = state['secondarySignalCombo'] if state['primaryGainCheck']: task['primaryGain'] = state['primaryGainSpin'] if state['secondaryGainCheck']: task['secondaryGain'] = state['secondaryGainSpin'] if mode != 'I=0': ## Must scale command to V or A before sending to task system. wave = self.getSingleWave(params) if wave is not None: task['command'] = wave if state['holdingCheck']: task['holding'] = state['holdingSpin'] #print "Task:", task return task def getSingleWave(self, params=None): state = self.stateGroup.state() h = state['holdingSpin'] #if state['holdingCheck']: #h = state['holdingSpin'] #else: #h = 0.0 self.ui.waveGeneratorWidget.setOffset(h) #self.ui.waveGeneratorWidget.setScale(self.cmdScale) ## waveGenerator generates values in V or A wave = self.ui.waveGeneratorWidget.getSingle(self.rate, self.numPts, params) if wave is None: return None #if state['holdingCheck']: #wave += (state['holdingSpin'] / self.cmdScale) return wave def getMode(self): if self.ui.icModeRadio.isChecked(): self.mode = 'IC' elif self.ui.i0ModeRadio.isChecked(): self.mode = 'I=0' else: self.mode = 'VC' return self.mode def setMode(self, mode=None): if mode != self.mode: oldMode = self.mode if mode is None: mode = self.getMode() #print "Set mode to", mode # set radio button if mode == 'IC': self.ui.icModeRadio.setChecked(True) elif mode == 'I=0': self.ui.i0ModeRadio.setChecked(True) else: self.ui.vcModeRadio.setChecked(True) # update signal lists self.stateGroup.blockSignals(True) sigs = self.dev.listSignals(mode) #print "Signals:", sigs #print "-------" for s, c in [(sigs[0], self.ui.primarySignalCombo),(sigs[1], self.ui.secondarySignalCombo)]: c.clear() for ss in s: c.addItem(ss) self.stateGroup.blockSignals(False) #self.ui.primarySignalCombo.clear() #for s in self.modeSignalList['primary'][mode]: #self.ui.primarySignalCombo.addItem(s) #self.ui.secondarySignalCombo.clear() #for s in self.modeSignalList['secondary'][mode]: #self.ui.secondarySignalCombo.addItem(s) # Disable signal, holding, and gain checks (only when switching between v and i modes) if mode == 'VC' or oldMode == 'VC': self.ui.primarySignalCheck.setChecked(False) self.ui.secondarySignalCheck.setChecked(False) self.ui.holdingCheck.setChecked(False) self.ui.holdingSpin.setValue(0.0) self.ui.primaryGainCheck.setChecked(False) self.ui.secondaryGainCheck.setChecked(False) # update unit labels and scaling if mode == 'VC': newUnit = 'V' oldUnit = 'A' #self.cmdScale = 1e-3 #self.inpScale = 1e-12 spinOpts = dict(suffix='V', siPrefix=True, dec=True, step=0.5, minStep=1e-3) self.ui.waveGeneratorWidget.setMeta('y', **spinOpts) self.ui.waveGeneratorWidget.setMeta('xy', units='V*s', siPrefix=True, dec=True, step=0.5, minStep=1e-6) else: newUnit = 'A' oldUnit = 'V' #self.cmdScale = 1e-12 #self.inpScale = 1e-3 spinOpts = dict(suffix='A', siPrefix=True, dec=True, step=0.5, minStep=1e-12) self.ui.waveGeneratorWidget.setMeta('y', **spinOpts) self.ui.waveGeneratorWidget.setMeta('xy', units='C', siPrefix=True, dec=True, step=0.5, minStep=1e-15) #self.stateGroup.setScale(self.ui.holdingSpin, 1./self.cmdScale) self.ui.holdingSpin.setOpts(**spinOpts) #self.ui.waveGeneratorWidget.setScale(self.cmdScale) for l in self.unitLabels: text = str(l.text()) l.setText(text.replace(oldUnit, newUnit)) self.ui.topPlotWidget.setLabel('left', units=oldUnit) self.ui.bottomPlotWidget.setLabel('left', units=newUnit) ## Hide stim plot for I=0 mode if mode == 'I=0': self.ui.bottomPlotWidget.hide() else: self.ui.bottomPlotWidget.show() self.devStateChanged() self.mode = mode def setSignals(self, pri, sec): #print "setSignals", pri, sec for c, s in [(self.ui.primarySignalCombo, pri), (self.ui.secondarySignalCombo, sec)]: if s is None: continue ind = c.findText(s) if ind == -1: for i in range(c.count()): print c.itemText(i) raise Exception('Signal "%s" does not exist' % s) c.setCurrentIndex(ind) def handleResult(self, result, params): if self.resetInpPlots: self.resetInpPlots = False self.clearInpPlots() ## Plot the results #plot = self.ui.topPlotWidget.plot(result['primary'].view(numpy.ndarray) / self.inpScale, x=result.xvals('Time'), params=params) plot = self.ui.topPlotWidget.plot(result['primary'].view(numpy.ndarray), x=result.xvals('Time'), params=params) def quit(self): TaskGui.quit(self) if not sip.isdeleted(self.daqUI): QtCore.QObject.disconnect(self.daqUI, QtCore.SIGNAL('changed'), self.daqChanged) self.ui.topPlotWidget.close() self.ui.bottomPlotWidget.close()
class DAQGenericTaskGui(TaskGui): #sigSequenceChanged = QtCore.Signal(object) ## defined upstream def __init__(self, dev, task, ownUi=True): TaskGui.__init__(self, dev, task) self.plots = weakref.WeakValueDictionary() self.channels = {} if ownUi: self.ui = Ui_Form() self.ui.setupUi(self) self.stateGroup = WidgetGroup([ (self.ui.topSplitter, 'splitter1'), (self.ui.controlSplitter, 'splitter2'), (self.ui.plotSplitter, 'splitter3'), ]) self.createChannelWidgets(self.ui.controlSplitter, self.ui.plotSplitter) self.ui.topSplitter.setStretchFactor(0, 0) self.ui.topSplitter.setStretchFactor(1, 1) else: ## If ownUi is False, then the UI is created elsewhere and createChannelWidgets must be called from there too. self.stateGroup = None def createChannelWidgets(self, ctrlParent, plotParent): ## Create plots and control widgets for ch in self.dev._DGConfig: (w, p) = self.createChannelWidget(ch) plotParent.addWidget(p) ctrlParent.addWidget(w) def createChannelWidget(self, ch, daqName=None): conf = self.dev._DGConfig[ch] p = PlotWidget(self) units = '' if 'units' in conf: units = conf['units'] p.setLabel('left', text=ch, units=units) self.plots[ch] = p p.registerPlot(self.dev.name() + '.' + ch) if conf['type'] in ['ao', 'do']: w = OutputChannelGui(self, ch, conf, p, self.dev, self.taskRunner, daqName) w.sigSequenceChanged.connect(self.sequenceChanged) elif conf['type'] in ['ai', 'di']: w = InputChannelGui(self, ch, conf, p, self.dev, self.taskRunner, daqName) else: raise Exception("Unrecognized device type '%s'" % conf['type']) self.channels[ch] = w return (w, p) def saveState(self): if self.stateGroup is not None: state = self.stateGroup.state().copy() else: state = {} state['channels'] = {} for ch in self.channels: state['channels'][ch] = self.channels[ch].saveState() return state def restoreState(self, state): try: if self.stateGroup is not None: self.stateGroup.setState(state) for ch in state['channels']: try: self.channels[ch].restoreState(state['channels'][ch]) except KeyError: printExc( "Warning: Cannot restore state for channel %s.%s (channel does not exist on this device)" % (self.dev.name(), ch)) continue except: printExc('Error while restoring GUI state:') def listSequence(self): ## returns sequence parameter names and lengths l = {} for ch in self.channels: chl = self.channels[ch].listSequence() for k in chl: l[ch + '.' + k] = chl[k] return l def sequenceChanged(self): self.sigSequenceChanged.emit(self.dev.name()) def taskStarted(self, params): ## automatically invoked from TaskGui ## Pull out parameters for this device params = dict([(p[1], params[p]) for p in params if p[0] == self.dev.name()]) for ch in self.channels: ## Extract just the parameters the channel will need chParams = {} search = ch + '.' for k in params: if k[:len(search)] == search: chParams[k[len(search):]] = params[k] self.channels[ch].taskStarted(chParams) def taskSequenceStarted(self): ## automatically invoked from TaskGui for ch in self.channels: self.channels[ch].taskSequenceStarted() def generateTask(self, params=None): if params is None: params = {} p = {} for ch in self.channels: ## Extract just the parameters the channel will need chParams = {} search = ch + '.' for k in params: if k[:len(search)] == search: chParams[k[len(search):]] = params[k] ## request the task from the channel p[ch] = self.channels[ch].generateTask(chParams) return p def handleResult(self, result, params): if result is None: return for ch in self.channels: if result.hasColumn(0, ch): self.channels[ch].handleResult(result[ch], params) def getChanHolding(self, chan): """Return the holding value that this channel will use when the task is run.""" return self.dev.getChanHolding(chan) def quit(self): TaskGui.quit(self) for ch in self.channels: self.channels[ch].quit()