class AnalysisDefinitionElement(object): def __init__(self): self.name = '' self.enabled = True self.evaluation = None self.fitfunctionName = None self.pushVariables = SequenceDict() self.fitfunction = None self.fitfunctionCache = dict() def __setstate__(self, state): self.__dict__ = state self.__dict__.setdefault('name', '') stateFields = [ 'name', 'enabled', 'evaluation', 'fitfunctionName', 'pushVariables', 'fitfunction', 'fitfunctionCache' ] def __eq__(self, other): return isinstance(other, self.__class__) and tuple( getattr(self, field) for field in self.stateFields) == tuple( getattr(other, field) for field in self.stateFields) def __ne__(self, other): return not self == other def __hash__(self): return hash(tuple(getattr(self, field) for field in self.stateFields)) def pushVariableValues(self): """get all push variable values that are within the bounds, no re-evaluation""" pushVarValues = list() failedList = [] for pushvar in list(self.pushVariables.values()): pushRecord, failedRecord = pushvar.pushRecord() failedList.extend(failedRecord) pushVarValues.extend(pushRecord) return pushVarValues, failedList def updatePushVariables(self, replacements): for pushvar in self.pushVariables.values(): pushvar.evaluate(replacements)
class AnalysisDefinitionElement(object): def __init__(self): self.name = '' self.enabled = True self.evaluation = None self.fitfunctionName = None self.pushVariables = SequenceDict() self.fitfunction = None self.fitfunctionCache = dict() def __setstate__(self, state): self.__dict__ = state self.__dict__.setdefault('name', '') stateFields = ['name', 'enabled', 'evaluation', 'fitfunctionName', 'pushVariables', 'fitfunction', 'fitfunctionCache'] def __eq__(self, other): return isinstance(other, self.__class__) and tuple(getattr(self, field) for field in self.stateFields)==tuple(getattr(other, field) for field in self.stateFields) def __ne__(self, other): return not self == other def __hash__(self): return hash(tuple(getattr(self, field) for field in self.stateFields)) def pushVariableValues(self): """get all push variable values that are within the bounds, no re-evaluation""" pushVarValues = list() failedList = [] for pushvar in list(self.pushVariables.values()): pushRecord, failedRecord = pushvar.pushRecord() failedList.extend( failedRecord ) pushVarValues.extend( pushRecord ) return pushVarValues, failedList def updatePushVariables(self, replacements ): for pushvar in self.pushVariables.values(): pushvar.evaluate( replacements )
class VoltageGlobalAdjust(VoltageGlobalAdjustForm, VoltageGlobalAdjustBase): updateOutput = QtCore.pyqtSignal(object, object) outputChannelsChanged = QtCore.pyqtSignal(object) _channelParams = {} def __init__(self, config, globalDict, parent=None): VoltageGlobalAdjustForm.__init__(self) VoltageGlobalAdjustBase.__init__(self, parent) self.config = config self.configname = 'VoltageGlobalAdjust.Settings' self.settings = self.config.get(self.configname, Settings()) self.globalAdjustDict = SequenceDict() self.myLabelList = list() self.myBoxList = list() self.historyCategory = 'VoltageGlobalAdjust' self.adjustHistoryName = None self.globalDict = globalDict self.adjustCache = self.config.get(self.configname + ".cache", dict()) self.savedValue = defaultdict(lambda: None) self.displayValueObservable = defaultdict(lambda: Observable()) def setupUi(self, parent): VoltageGlobalAdjustForm.setupUi(self, parent) self.gainBox.setValue(self.settings.gain) self.gainBox.valueChanged.connect(self.onGainChanged) self.tableModel = VoltageGlobalAdjustTableModel( self.globalAdjustDict, self.globalDict) self.tableView.setModel(self.tableModel) self.tableView.setSortingEnabled(True) # triggers sorting self.delegate = MagnitudeSpinBoxDelegate(self.globalDict) self.tableView.setItemDelegateForColumn(1, self.delegate) def onGainChanged(self, gain): self.settings.gain = gain self.updateOutput.emit(self.globalAdjustDict, self.settings.gain) def setupGlobalAdjust(self, name, adjustDict): if name != self.adjustHistoryName: self.adjustCache[self.adjustHistoryName] = [ v.data for v in list(self.globalAdjustDict.values()) ] self.settings.gainCache[ self.adjustHistoryName] = self.settings.gain self.settings.gain = self.settings.gainCache.get( name, self.settings.gain) if name in self.adjustCache: for data in self.adjustCache[name]: if data[0] in adjustDict: adjustDict[data[0]].data = data self.adjustHistoryName = name self.globalAdjustDict = adjustDict for name, adjust in self.globalAdjustDict.items(): try: adjust.valueChanged.connect(self.onValueChanged, QtCore.Qt.UniqueConnection) except: pass self.tableModel.setGlobalAdjust(adjustDict) self.outputChannelsChanged.emit(self.outputChannels()) self.gainBox.setValue(self.settings.gain) #self.updateOutput.emit(self.globalAdjustDict, self.settings.gain) def onValueChanged(self, name, value, string, origin): if origin == 'recalculate': self.tableModel.valueRecalcualted(name) self.globalAdjustDict[name]._value = float( self.globalAdjustDict[name]._value) self.updateOutput.emit(self.globalAdjustDict, self.settings.gain) def saveConfig(self): self.config[self.configname] = self.settings self.adjustCache[self.adjustHistoryName] = [ v.data for v in list(self.globalAdjustDict.values()) ] self.config[self.configname + ".cache"] = self.adjustCache def setValue(self, channel, value): self.globalAdjustDict[channel].value = value return value def getValue(self, channel): return self.globalAdjustDict[channel].value def currentValue(self, channel): return self.globalAdjustDict[channel].value def saveValue(self, channel): self.savedValue[channel] = self.globalAdjustDict[channel].value def restoreValue(self, channel): if self.savedValue[channel] is not None: self.globalAdjustDict[channel].value = self.savedValue[channel] return True def strValue(self, channel): adjust = self.globalAdjustDict[channel] return adjust.string if adjust.hasDependency else None def setStrValue(self, channel, value): pass def outputChannels(self): self._outputChannels = dict( ((channelName, VoltageOutputChannel( self, None, channelName, self.globalDict, )) for channelName in self.globalAdjustDict.keys())) return self._outputChannels
class VoltageBlender(QtCore.QObject): dataChanged = QtCore.pyqtSignal(int, int, int, int) dataError = QtCore.pyqtSignal(object) shuttlingOnLine = QtCore.pyqtSignal(float) def __init__(self, globalDict, dacController): logger = logging.getLogger(__name__) super(VoltageBlender, self).__init__() self.dacController = dacController if hardwareObjName==FPGAObjName and hardwareDict: self.hardware = FPGAHardware(self.dacController) elif hardwareObjName==NIObjName and hardwareDict: self.hardware = NIHardware() else: self.hardware = NoneHardware() self.itf = itfParser() self.lines = list() # a list of lines with numpy arrays self.adjustDict = SequenceDict() # names of the lines presented as possible adjusts self.adjustLines = [] self.lineGain = 1.0 self.globalGain = 1.0 self.lineno = 0 self.mappingpath = None self.outputVoltage = None self.electrodes = None self.aoNums = None self.dsubNums = None self.tableHeader = list() self.globalDict = globalDict self.adjustGain = 1.0 self.localAdjustVoltages = list() self.uploadedDataHash = None def currentData(self): return self.electrodes, self.aoNums, self.dsubNums, self.outputVoltage def loadMapping(self, path): path = project.findFile(path) self.itf.eMapFilePath = path self.mappingpath = path self.electrodes, self.aoNums, self.dsubNums = self.itf._getEmapData() self.dataChanged.emit(0, 0, len(self.electrodes)-1, 3) def loadVoltage(self, path): channelCount = self.hardware.channelCount if self.hardware else 0 self.itf.open(path) self.lines = list() for _ in range(self.itf.getNumLines()): line = self.itf.eMapReadLine() for index, value in enumerate(line): if math.isnan(value): line[index]=0 line = numpy.append( line, [0.0]*max(0, channelCount-len(line))) self.lines.append( line ) self.tableHeader = self.itf.tableHeader self.itf.close() self.dataChanged.emit(0, 0, len(self.electrodes)-1, 3) def loadGlobalAdjust(self, path): channelCount = self.hardware.channelCount if self.hardware else 0 self.adjustLines = list() self.adjustDict = SequenceDict() itf = itfParser() itf.eMapFilePath = self.mappingpath itf.open(path) for _ in range(itf.getNumLines()): line = itf.eMapReadLine() for index, value in enumerate(line): if math.isnan(value): line[index]=0 line = numpy.append( line, [0.0]*max(0, channelCount-len(line))) self.adjustLines.append( line ) for name, value in itf.meta.items(): try: if int(value)<len(self.adjustLines): self.adjustDict[name] = AdjustValue(name=name, line=int(value), globalDict=self.globalDict) except ValueError: pass # if it's not an int we will ignore it here itf.close() self.dataChanged.emit(0, 0, len(self.electrodes)-1, 3) def loadLocalAdjust(self, localAdjustList, forceupdate=list() ): channelCount = self.hardware.channelCount if self.hardware else 0 for index, record in enumerate(localAdjustList): path = record.path if index in forceupdate or record.solutionPath != record.path: if os.path.exists(path): linelist = list() itf = itfParser() itf.eMapFilePath = self.mappingpath itf.open(path) for _ in range(itf.getNumLines()): line = itf.eMapReadLine() for index, value in enumerate(line): if math.isnan(value): line[index]=0 line = numpy.append( line, [0.0]*max(0, channelCount-len(line))) linelist.append( line ) itf.close() record.solution = linelist record.solutionPath = path else: logging.getLogger(__name__).warning("Local Adjust file '{0}' not found".format(path)) if self.electrodes: self.dataChanged.emit(0, 0, len(self.electrodes)-1, 3) self.localAdjustVoltages = localAdjustList def setAdjust(self, adjust, gain): self.adjustDict = adjust self.adjustGain = float(gain) self.applyLine(self.lineno, self.lineGain, self.globalGain) def setLocalAdjust(self, localAdjustDict ): self.localAdjustVoltages = localAdjustDict self.applyLine(self.lineno, self.lineGain, self.globalGain) def applyLine(self, lineno, lineGain, globalGain, updateHardware=True ): if updateHardware: line = self.calculateLine(float(lineno), float(lineGain), float(globalGain)) try: self.hardware.applyLine(line) self.outputVoltage = line self.lineno = lineno self.dataChanged.emit(0, 1, len(self.electrodes)-1, 1) except (HardwareException, DACControllerException) as e: logging.getLogger(__name__).exception("Exception") outOfRange = line>10 outOfRange |= line<-10 self.dataError.emit(outOfRange.tolist()) else: self.lineno = lineno def calculateLine(self, lineno, lineGain, globalGain): line = self.blendLines(lineno, lineGain) localadjustline = self.blendLocalAdjustLines(lineno) self.lineGain = lineGain self.globalGain = globalGain #self.lineno = lineno line = self.adjustLine( line ) if len(localadjustline) > 0: line = numpy.add( line, localadjustline ) line *= self.globalGain return line def shuttle(self, definition, cont): logger = logging.getLogger(__name__) if not self.hardware.nativeShuttling: for start, _, edge, _ in definition: fromLine, toLine = (edge.startLine, edge.stopLine) if start==edge.startName else (edge.stopLine, edge.startLine) for line in numpy.linspace(fromLine, toLine, edge.steps+2, True): self.applyLine(line, self.lineGain, self.globalGain) logger.debug( "shuttling applied line {0}".format( line ) ) self.shuttlingOnLine.emit(line) else: # this stuff does not work yet if definition: logger.info( "Starting finite shuttling" ) globaladjust = [0]*len(self.lines[0]) self.adjustLine(globaladjust) self.hardware.shuttlePath( [(index, start!=edge.startName, True) for start, _, edge, index in definition] ) start, _, edge, _ = definition[-1] self.shuttleTo = edge.startLine if start!=edge.startName else edge.stopLine self.shuttlingOnLine.emit(self.shuttleTo) self.outputVoltage = line = self.calculateLine( float(self.shuttleTo), float(self.lineGain), float(self.globalGain) ) self.dataChanged.emit(0, 1, len(self.electrodes)-1, 1) def adjustLine(self, line): offset = numpy.zeros(len(line)) for adjust in self.adjustDict.values(): offset += self.adjustLines[adjust.line] * float(adjust.floatValue) offset *= self.adjustGain return (line+offset) def blendLines(self, lineno, lineGain): if self.lines: left = int(math.floor(lineno)) right = int(math.ceil(lineno)) convexc = lineno-left return (self.lines[left]*(1-convexc) + self.lines[right]*convexc)*lineGain return None def blendLocalAdjustLines(self, lineno): channelCount = self.hardware.channelCount if self.hardware else 0 left = int(math.floor(lineno)) right = int(math.ceil(lineno)) convexc = lineno-left result = numpy.zeros(channelCount) for record in self.localAdjustVoltages: if record.solution: if isfunction(record.gain.value): result = numpy.add(result, (record.solution[left]*(1-convexc)*record.gain.value(left) + record.solution[right]*convexc*record.gain.value(right))) else: result = numpy.add(result, (record.solution[left]*(1-convexc) + record.solution[right]*convexc)*record.gainValue) return result def close(self): self.hardware.close() def writeShuttleLookup(self, edgeList, address=0): if(self.dacController.isOpen): self.dacController.writeShuttleLookup(edgeList, address) def writeData(self, shuttlingGraph): towrite = list() startline = 1 currentline = startline lineGain = float(self.lineGain) globalGain = float(self.globalGain) if shuttlingGraph: for edge in shuttlingGraph: towrite.extend( [self.calculateLine(lineno, lineGain, globalGain ) for lineno in edge.iLines() ] ) edge.interpolStartLine = currentline currentline = startline+len(towrite) edge.interpolStopLine = currentline data = self.dacController.writeVoltages(1, towrite ) self.dacController.verifyVoltages(1, data ) self.uploadedDataHash = self.shuttlingDataHash() stateFields = ('lineGain', 'globalGain', 'adjustGain') def shuttlingDataHash(self): h = hash(itertools.chain((getattr(self, field).to_tuple() for field in self.stateFields), (q.to_tuple() for q in self.adjustDict.values()), (q.to_tuple() for q in self.localAdjustVoltages))) logging.getLogger(__name__).info("Shuttling Hash: {0:x}".format(h)) return h def shuttlingDataValid(self): return self.shuttlingDataHash()==self.uploadedDataHash def trigger(self): self.dacController.triggerShuttling()
class VoltageBlender(QtCore.QObject): dataChanged = QtCore.pyqtSignal(int, int, int, int) dataError = QtCore.pyqtSignal(object) shuttlingOnLine = QtCore.pyqtSignal(float) def __init__(self, globalDict, dacController): logger = logging.getLogger(__name__) super(VoltageBlender, self).__init__() self.dacController = dacController if hardwareObjName == FPGAObjName and hardwareDict: self.hardware = FPGAHardware(self.dacController) elif hardwareObjName == NIObjName and hardwareDict: self.hardware = NIHardware() else: self.hardware = NoneHardware() self.itf = itfParser() self.lines = list() # a list of lines with numpy arrays self.adjustDict = SequenceDict( ) # names of the lines presented as possible adjusts self.adjustLines = [] self.lineGain = 1.0 self.globalGain = 1.0 self.lineno = 0 self.mappingpath = None self.outputVoltage = None self.electrodes = None self.aoNums = None self.dsubNums = None self.tableHeader = list() self.globalDict = globalDict self.adjustGain = 1.0 self.localAdjustVoltages = list() self.uploadedDataHash = None def currentData(self): return self.electrodes, self.aoNums, self.dsubNums, self.outputVoltage def loadMapping(self, path): path = project.findFile(path) self.itf.eMapFilePath = path self.mappingpath = path self.electrodes, self.aoNums, self.dsubNums = self.itf._getEmapData() self.dataChanged.emit(0, 0, len(self.electrodes) - 1, 3) def loadVoltage(self, path): channelCount = self.hardware.channelCount if self.hardware else 0 self.itf.open(path) self.lines = list() for _ in range(self.itf.getNumLines()): line = self.itf.eMapReadLine() for index, value in enumerate(line): if math.isnan(value): line[index] = 0 line = numpy.append(line, [0.0] * max(0, channelCount - len(line))) self.lines.append(line) self.tableHeader = self.itf.tableHeader self.itf.close() self.dataChanged.emit(0, 0, len(self.electrodes) - 1, 3) def loadGlobalAdjust(self, path): channelCount = self.hardware.channelCount if self.hardware else 0 self.adjustLines = list() self.adjustDict = SequenceDict() itf = itfParser() itf.eMapFilePath = self.mappingpath itf.open(path) for _ in range(itf.getNumLines()): line = itf.eMapReadLine() for index, value in enumerate(line): if math.isnan(value): line[index] = 0 line = numpy.append(line, [0.0] * max(0, channelCount - len(line))) self.adjustLines.append(line) for name, value in itf.meta.items(): try: if int(value) < len(self.adjustLines): self.adjustDict[name] = AdjustValue( name=name, line=int(value), globalDict=self.globalDict) except ValueError: pass # if it's not an int we will ignore it here itf.close() self.dataChanged.emit(0, 0, len(self.electrodes) - 1, 3) def loadLocalAdjust(self, localAdjustList, forceupdate=list()): channelCount = self.hardware.channelCount if self.hardware else 0 for index, record in enumerate(localAdjustList): path = record.path if index in forceupdate or record.solutionPath != record.path: if os.path.exists(path): linelist = list() itf = itfParser() itf.eMapFilePath = self.mappingpath itf.open(path) for _ in range(itf.getNumLines()): line = itf.eMapReadLine() for index, value in enumerate(line): if math.isnan(value): line[index] = 0 line = numpy.append(line, [0.0] * max(0, channelCount - len(line))) linelist.append(line) itf.close() record.solution = linelist record.solutionPath = path else: logging.getLogger(__name__).warning( "Local Adjust file '{0}' not found".format(path)) if self.electrodes: self.dataChanged.emit(0, 0, len(self.electrodes) - 1, 3) self.localAdjustVoltages = localAdjustList def setAdjust(self, adjust, gain): self.adjustDict = adjust self.adjustGain = float(gain) self.applyLine(self.lineno, self.lineGain, self.globalGain) def setLocalAdjust(self, localAdjustDict): self.localAdjustVoltages = localAdjustDict self.applyLine(self.lineno, self.lineGain, self.globalGain) def applyLine(self, lineno, lineGain, globalGain, updateHardware=True): if updateHardware: line = self.calculateLine(float(lineno), float(lineGain), float(globalGain)) try: self.hardware.applyLine(line) self.outputVoltage = line self.lineno = lineno self.dataChanged.emit(0, 1, len(self.electrodes) - 1, 1) except (HardwareException, DACControllerException) as e: logging.getLogger(__name__).exception("Exception") outOfRange = line > 10 outOfRange |= line < -10 self.dataError.emit(outOfRange.tolist()) else: self.lineno = lineno def calculateLine(self, lineno, lineGain, globalGain): line = self.blendLines(lineno, lineGain) localadjustline = self.blendLocalAdjustLines(lineno) self.lineGain = lineGain self.globalGain = globalGain #self.lineno = lineno line = self.adjustLine(line) if len(localadjustline) > 0: line = numpy.add(line, localadjustline) line *= self.globalGain return line def shuttle(self, definition, cont): logger = logging.getLogger(__name__) if not self.hardware.nativeShuttling: for start, _, edge, _ in definition: fromLine, toLine = ( edge.startLine, edge.stopLine) if start == edge.startName else ( edge.stopLine, edge.startLine) for line in numpy.linspace(fromLine, toLine, edge.steps + 2, True): self.applyLine(line, self.lineGain, self.globalGain) logger.debug("shuttling applied line {0}".format(line)) self.shuttlingOnLine.emit(line) else: # this stuff does not work yet if definition: logger.info("Starting finite shuttling") globaladjust = [0] * len(self.lines[0]) self.adjustLine(globaladjust) self.hardware.shuttlePath([ (index, start != edge.startName, True) for start, _, edge, index in definition ]) start, _, edge, _ = definition[-1] self.shuttleTo = edge.startLine if start != edge.startName else edge.stopLine self.shuttlingOnLine.emit(self.shuttleTo) self.outputVoltage = line = self.calculateLine( float(self.shuttleTo), float(self.lineGain), float(self.globalGain)) self.dataChanged.emit(0, 1, len(self.electrodes) - 1, 1) def adjustLine(self, line): offset = numpy.zeros(len(line)) for adjust in self.adjustDict.values(): offset += self.adjustLines[adjust.line] * float(adjust.floatValue) offset *= self.adjustGain return (line + offset) def blendLines(self, lineno, lineGain): if self.lines: left = int(math.floor(lineno)) right = int(math.ceil(lineno)) convexc = lineno - left return (self.lines[left] * (1 - convexc) + self.lines[right] * convexc) * lineGain return None def blendLocalAdjustLines(self, lineno): channelCount = self.hardware.channelCount if self.hardware else 0 left = int(math.floor(lineno)) right = int(math.ceil(lineno)) convexc = lineno - left result = numpy.zeros(channelCount) for record in self.localAdjustVoltages: if record.solution: if isfunction(record.gain.data[1]): result = numpy.add( result, (record.solution[left] * (1 - convexc) * record.gainValue(left) + record.solution[right] * convexc * record.gainValue(right))) else: result = numpy.add( result, (record.solution[left] * (1 - convexc) + record.solution[right] * convexc) * record.gainValue) return result def close(self): self.hardware.close() def writeShuttleLookup(self, edgeList, address=0): if (self.dacController.isOpen): self.dacController.writeShuttleLookup(edgeList, address) def writeData(self, shuttlingGraph): towrite = list() startline = 1 currentline = startline lineGain = float(self.lineGain) globalGain = float(self.globalGain) if shuttlingGraph: for edge in shuttlingGraph: towrite.extend([ self.calculateLine(lineno, lineGain, globalGain) for lineno in edge.iLines() ]) edge.interpolStartLine = currentline currentline = startline + len(towrite) edge.interpolStopLine = currentline data = self.dacController.writeVoltages(1, towrite) self.dacController.verifyVoltages(1, data) self.uploadedDataHash = self.shuttlingDataHash() stateFields = ('lineGain', 'globalGain', 'adjustGain') def shuttlingDataHash(self): h = hash( itertools.chain((getattr(self, field).to_tuple() for field in self.stateFields), (q.to_tuple() for q in self.adjustDict.values()), (q.to_tuple() for q in self.localAdjustVoltages))) logging.getLogger(__name__).info("Shuttling Hash: {0:x}".format(h)) return h def shuttlingDataValid(self): return self.shuttlingDataHash() == self.uploadedDataHash def trigger(self): self.dacController.triggerShuttling()
class ExperimentUi(WidgetContainerBase,WidgetContainerForm): levelNameList = ["debug", "info", "warning", "error", "critical"] levelValueList = [logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL] def __init__(self, config, project): self.config = config self.project = project super(ExperimentUi, self).__init__() self.settings = FPGASettings() self.loggingLevel = config.get('Settings.loggingLevel',logging.INFO) self.consoleMaximumLines = config.get('Settings.consoleMaximumLinesNew',100) self.consoleEnable = config.get('Settings.consoleEnable',False) self.shutterNameDict = config.get('Settings.ShutterNameDict', ChannelNameDict()) if self.shutterNameDict.__class__.__name__ == 'ChannelNameMap': self.shutterNameDict = ChannelNameDict( self.shutterNameDict.names ) if self.shutterNameDict.customDict.__class__.__name__ == 'ChannelNameMap': self.shutterNameDict = ChannelNameDict(self.shutterNameDict.customDict._fwd, self.shutterNameDict.defaultDict ) self.shutterNameSignal = DataChanged() self.triggerNameDict = config.get('Settings.TriggerNameDict', ChannelNameDict()) if self.triggerNameDict.__class__.__name__ == 'ChannelNameMap': self.triggerNameDict = ChannelNameDict( self.triggerNameDict.names ) self.triggerNameSignal = DataChanged() if self.loggingLevel not in self.levelValueList: self.loggingLevel = logging.INFO self.dbConnection = project.dbConnection self.objectListToSaveContext = list() self.voltageControlWindow = None localpath = getProject().configDir+'/UserFunctions/' for filename in Path(localpath.replace('\\','/')).glob('**/*.py'): try: importlib.machinery.SourceFileLoader("CustomFunctions", str(filename).replace('\\','/')).load_module() except SyntaxError as e: SyntaxError('Failed to load {0}'.format(str(filename))) def __enter__(self): self.pulser = PulserHardware() return self def __exit__(self, excepttype, value, traceback): self.pulser.shutdown() return False def setupUi(self, parent): super(ExperimentUi,self).setupUi(parent) self.dockWidgetConsole.hide() self.loggerUi = LoggerLevelsUi(self.config) self.loggerUi.setupUi(self.loggerUi) self.loggerDock = QtWidgets.QDockWidget("Logging") self.loggerDock.setWidget(self.loggerUi) self.loggerDock.setObjectName("_LoggerDock") self.addDockWidget( QtCore.Qt.RightDockWidgetArea, self.loggerDock) self.loggerDock.hide() logger = logging.getLogger() self.exceptionToolBar.addWidget(ExceptionLogButton()) self.warningLogButton = LogButton(messageIcon=":/petersIcons/icons/Warning.png", messageName="warnings") self.exceptionToolBar.addWidget(self.warningLogButton) qtWarningButtonHandler.textWritten.connect(self.warningLogButton.addMessage) # Setup Console Dockwidget self.levelComboBox.addItems(self.levelNameList) self.levelComboBox.currentIndexChanged[int].connect( self.setLoggingLevel ) self.levelComboBox.setCurrentIndex( self.levelValueList.index(self.loggingLevel) ) self.consoleClearButton.clicked.connect( self.onClearConsole ) self.linesSpinBox.valueChanged.connect( self.onConsoleMaximumLinesChanged ) self.linesSpinBox.setValue( self.consoleMaximumLines ) self.checkBoxEnableConsole.stateChanged.connect( self.onEnableConsole ) self.checkBoxEnableConsole.setChecked( self.consoleEnable ) self.parent = parent self.tabDict = SequenceDict() if self.project.isEnabled('software', 'Memory Profiler'): self.memoryProfiler = MemoryProfiler(self) #determine if Voltages software is enabled and import class if it is self.voltagesEnabled = self.project.isEnabled('software', 'Voltages') if self.voltagesEnabled: from voltageControl.VoltageControl import VoltageControl #setup external parameters; import specific libraries if they are needed, popup warnings if selected hardware import fail import externalParameter.StandardExternalParameter import externalParameter.InterProcessParameters if self.project.isEnabled('hardware', 'Conex Motion'): try: import externalParameter.MotionParameter #@UnusedImport except ImportError: #popup on failed import importErrorPopup('Conex Motion') if self.project.isEnabled('hardware', 'APT Motion'): try: import externalParameter.APTInstruments # @UnusedImport externalParameter.APTInstruments.loadDll( list(self.project.hardware['APT Motion'].values())[0]['dllPath']) except Exception as e: # popup on failed import importErrorPopup('APT Motion error {0}'.format(e)) if self.project.isEnabled('hardware', 'Lab Brick'): try: import externalParameter.LabBrick # @UnusedImport externalParameter.LabBrick.loadDll( list(self.project.hardware['Lab Brick'].values())[0]['dllPath']) except Exception as e: # popup on failed import importErrorPopup('Lab Brick error {0}'.format(e)) from externalParameter.ExternalParameterBase import InstrumentDict # setup FPGAs self.setupFPGAs() # initialize PulseProgramUi pulserConfig = self.pulser.pulserConfiguration() self.shutterNameDict.defaultDict = pulserConfig.shutterBits if pulserConfig else dict() self.triggerNameDict.defaultDict = pulserConfig.triggerBits if pulserConfig else dict() self.counterNameDict = pulserConfig.counterBits if pulserConfig else dict() self.channelNameData = (self.shutterNameDict, self.shutterNameSignal, self.triggerNameDict, self.triggerNameSignal, self.counterNameDict ) self.pulseProgramDialog = PulseProgramUi.PulseProgramSetUi(self.config, self.channelNameData ) self.pulseProgramDialog.setupUi(self.pulseProgramDialog) # Global Variables self.globalVariablesUi = GlobalVariablesUi(self.config) self.globalVariablesUi.setupUi(self.globalVariablesUi) self.globalVariablesDock = QtWidgets.QDockWidget("Global Variables") self.globalVariablesDock.setObjectName("Global Variables") self.globalVariablesDock.setWidget( self.globalVariablesUi ) self.addDockWidget(QtCore.Qt.RightDockWidgetArea , self.globalVariablesDock) self.measurementLog = MeasurementLogUi(self.config, self.dbConnection) self.measurementLog.setupUi(self.measurementLog) #self.measurementLogDock = QtWidgets.QDockWidget("Measurement Log") #self.measurementLogDock.setWidget( self.measurementLog ) #self.measurementLogDock.setObjectName('_MeasurementLog') #self.addDockWidget( QtCore.Qt.BottomDockWidgetArea, self.measurementLogDock ) self.preferencesUi = PreferencesUi(config, self) self.preferencesUi.setupUi(self.preferencesUi) self.preferencesUiDock = QtGui.QDockWidget("Print Preferences") self.preferencesUiDock.setWidget(self.preferencesUi) self.preferencesUiDock.setObjectName("_preferencesUi") self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.preferencesUiDock) for widget, name in [(ScanExperiment.ScanExperiment(self.settings, self.pulser, self.globalVariablesUi, "ScanExperiment", toolBar=self.experimentToolBar, measurementLog=self.measurementLog, callWhenDoneAdjusting=self.callWhenDoneAdjusting, preferences=self.preferencesUi.preferences().printPreferences), "Scan") ]: widget.setupUi(widget, self.config) if hasattr(widget, 'setPulseProgramUi'): widget.setPulseProgramUi(self.pulseProgramDialog) if hasattr(widget, 'plotsChanged'): widget.plotsChanged.connect(self.initMenu) self.tabWidget.addTab(widget, name) self.tabDict[name] = widget widget.ClearStatusMessage.connect(self.statusbar.clearMessage) widget.StatusMessage.connect(self.statusbar.showMessage) widget.stashChanged.connect(self.onStashChanged) self.scanExperiment = self.tabDict["Scan"] self.shutterUi, self.shutterDockWidget = self.instantiateShutterUi(self.pulser, 'Shutters', "ShutterUi", self.config, self.globalVariablesUi.globalDict, self.shutterNameDict, self.shutterNameSignal) self.triggerUi = ShutterUi.TriggerUi(self.pulser, 'ShutterUi', 'trigger', self.config, (self.triggerNameDict, self.triggerNameSignal) ) self.triggerUi.offColor = QtGui.QColor(QtCore.Qt.white) self.triggerUi.setupUi(self.triggerUi) self.pulser.ppActiveChanged.connect( self.triggerUi.setDisabled ) self.triggerDockWidget.setWidget( self.triggerUi ) #AWGs enabledAWGDict = {displayName:className for displayName,className in AWGDevices.AWGDeviceDict.items() if self.project.isEnabled('hardware', displayName)} self.AWGUiDict = dict() if enabledAWGDict: AWGIcon = QtGui.QIcon() AWGPixmap = QtGui.QPixmap(":/other/icons/AWG.png") AWGIcon.addPixmap(AWGPixmap) AWGButton = QtWidgets.QToolButton() AWGButton.setIcon(AWGIcon) self.toolBar.addWidget(AWGButton) if len(enabledAWGDict) > 1: menu = QtWidgets.QMenu("AWG") AWGButton.setMenu(menu) AWGButton.setPopupMode(QtWidgets.QToolButton.InstantPopup) menu.setIcon(AWGIcon) self.menuWindows.addMenu(menu) for displayName, className in enabledAWGDict.items(): awgUi = AWGUi(getattr(AWGDevices, className), self.config, self.globalVariablesUi.globalDict, self.scanExperiment.pulseProgramUi) self.AWGUiDict[displayName] = awgUi awgUi.setupUi(awgUi) awgUi.varDictChanged.connect( partial(self.scanExperiment.updateScanTarget, displayName) ) self.scanExperiment.updateScanTarget( displayName, awgUi.varAsOutputChannelDict ) self.globalVariablesUi.valueChanged.connect( awgUi.evaluate ) action = QtWidgets.QAction(AWGIcon, displayName, self) action.triggered.connect(partial(self.onAWG, displayName)) if len(enabledAWGDict) > 1: menu.addAction(action) else: self.menuWindows.addAction(action) AWGButton.clicked.connect(action.trigger) ParameterUi, self.pulserParameterUiDock = self.instantiateParametersUi(self.pulser, "Pulser Parameters", "PulserParameterUi", self.config, self.globalVariablesUi.globalDict) self.objectListToSaveContext.append(ParameterUi) self.DDSUi, self.DDSDockWidget = self.instantiateDDSUi(self.pulser, "DDS", "DDSUi", self.config, self.globalVariablesUi.globalDict) self.objectListToSaveContext.append(self.DDSUi) self.DACUi, self.DACDockWidget = self.instantiateDACUi(self.pulser, "DAC", "dacUi", self.config, self.globalVariablesUi.globalDict) self.objectListToSaveContext.append(self.DACUi) # self.DDSUi9910 = DDSUi9910.DDSUi(self.config, self.pulser ) # self.DDSUi9910.setupUi(self.DDSUi9910) # self.DDS9910DockWidget.setWidget( self.DDSUi9910 ) # self.pulser.ppActiveChanged.connect( self.DDSUi9910.setDisabled ) #self.tabDict['Scan'].NeedsDDSRewrite.connect( self.DDSUi9910.onWriteAll ) self.instantiateAuxiliaryPulsers() self.valueHistoryUi = ValueHistoryUi(self.config, self.dbConnection) self.valueHistoryUi.setupUi( self.valueHistoryUi ) self.valueHistoryDock = QtWidgets.QDockWidget("Value History") self.valueHistoryDock.setWidget( self.valueHistoryUi ) self.valueHistoryDock.setObjectName("_valueHistory") self.addDockWidget( QtCore.Qt.RightDockWidgetArea, self.valueHistoryDock ) # tabify the dock widgets self.tabifyDockWidget( self.pulserParameterUiDock, self.preferencesUiDock) self.tabifyDockWidget( self.preferencesUiDock, self.triggerDockWidget ) self.tabifyDockWidget( self.triggerDockWidget, self.shutterDockWidget) self.tabifyDockWidget( self.shutterDockWidget, self.DDSDockWidget ) self.tabifyDockWidget( self.DDSDockWidget, self.DACDockWidget ) # self.tabifyDockWidget( self.DDSDockWidget, self.DDS9910DockWidget ) # self.tabifyDockWidget( self.DDS9910DockWidget, self.globalVariablesDock ) self.tabifyDockWidget( self.DACDockWidget, self.globalVariablesDock ) self.tabifyDockWidget( self.globalVariablesDock, self.valueHistoryDock ) self.triggerDockWidget.hide() self.preferencesUiDock.hide() self.ExternalParametersSelectionUi = ExternalParameterSelection.SelectionUi(self.config, self.globalVariablesUi.globalDict, classdict=InstrumentDict) self.ExternalParametersSelectionUi.setupUi( self.ExternalParametersSelectionUi ) self.ExternalParameterSelectionDock = QtWidgets.QDockWidget("Params Selection") self.ExternalParameterSelectionDock.setObjectName("_ExternalParameterSelectionDock") self.ExternalParameterSelectionDock.setWidget(self.ExternalParametersSelectionUi) self.addDockWidget( QtCore.Qt.RightDockWidgetArea, self.ExternalParameterSelectionDock) self.ExternalParametersUi = ExternalParameterUi.ControlUi(self.config, self.globalVariablesUi.globalDict) self.ExternalParametersUi.setupUi(self.ExternalParametersSelectionUi.outputChannels()) self.ExternalParameterDock = QtWidgets.QDockWidget("Params Control") self.ExternalParameterDock.setWidget(self.ExternalParametersUi) self.ExternalParameterDock.setObjectName("_ExternalParameterDock") self.addDockWidget( QtCore.Qt.RightDockWidgetArea, self.ExternalParameterDock) self.ExternalParametersSelectionUi.outputChannelsChanged.connect( self.ExternalParametersUi.setupParameters ) self.instrumentLoggingDisplay = InstrumentLoggingDisplay(self.config) self.instrumentLoggingDisplay.setupUi( self.ExternalParametersSelectionUi.inputChannels(), self.instrumentLoggingDisplay ) self.instrumentLoggingDisplayDock = QtWidgets.QDockWidget("Params Reading") self.instrumentLoggingDisplayDock.setObjectName("_ExternalParameterDisplayDock") self.instrumentLoggingDisplayDock.setWidget(self.instrumentLoggingDisplay) self.addDockWidget( QtCore.Qt.RightDockWidgetArea, self.instrumentLoggingDisplayDock) self.ExternalParametersSelectionUi.inputChannelsChanged.connect( self.instrumentLoggingDisplay.setupParameters ) self.ExternalParametersSelectionUi.outputChannelsChanged.connect( partial(self.scanExperiment.updateScanTarget, 'External') ) self.scanExperiment.updateScanTarget( 'External', self.ExternalParametersSelectionUi.outputChannels() ) self.todoList = TodoList( self.tabDict, self.config, self.getCurrentTab, self.switchTab, self.globalVariablesUi ) self.todoList.setupUi() self.todoListDock = QtWidgets.QDockWidget("Todo List") self.todoListDock.setWidget(self.todoList) self.todoListDock.setObjectName("_todoList") self.addDockWidget( QtCore.Qt.RightDockWidgetArea, self.todoListDock) self.tabifyDockWidget(self.valueHistoryDock, self.todoListDock) for name, widget in self.tabDict.items(): if hasattr( widget, 'scanConfigurationListChanged' ) and widget.scanConfigurationListChanged is not None: widget.scanConfigurationListChanged.connect( partial( self.todoList.populateMeasurementsItem, name) ) if hasattr( widget, 'evaluationConfigurationChanged' ) and widget.evaluationConfigurationChanged is not None: widget.evaluationConfigurationChanged.connect( partial( self.todoList.populateEvaluationItem, name) ) if hasattr( widget, 'analysisConfigurationChanged' ) and widget.analysisConfigurationChanged is not None: widget.analysisConfigurationChanged.connect( partial( self.todoList.populateAnalysisItem, name) ) #tabify external parameters controls self.tabifyDockWidget(self.ExternalParameterSelectionDock, self.ExternalParameterDock) self.tabifyDockWidget(self.ExternalParameterDock, self.instrumentLoggingDisplayDock) self.tabWidget.currentChanged.connect(self.onCurrentChanged) self.actionClear.triggered.connect(self.onClear) self.actionPause.triggered.connect(self.onPause) #Save and load actions self.actionSave_GUI.triggered.connect(self.onSaveGUI) self.actionSave_GUI_Yaml.triggered.connect(self.onSaveGUIYaml) self.actionStart.triggered.connect(self.onStart) self.actionStop.triggered.connect(self.onStop) self.actionAbort.triggered.connect(self.onAbort) self.actionExit.triggered.connect(self.onClose) self.actionContinue.triggered.connect(self.onContinue) self.actionPulses.triggered.connect(self.onPulses) self.actionReload.triggered.connect(self.onReload) self.actionProject.triggered.connect( self.onProjectSelection) self.actionDocumentation.triggered.connect(self.onShowDocumentation) if self.voltagesEnabled: self.actionVoltageControl.triggered.connect(self.onVoltageControl) else: self.actionVoltageControl.setDisabled(True) self.actionVoltageControl.setVisible(False) self.actionScripting.triggered.connect(self.onScripting) self.actionUserFunctions.triggered.connect(self.onUserFunctionsEditor) self.actionMeasurementLog.triggered.connect(self.onMeasurementLog) self.actionDedicatedCounters.triggered.connect(self.showDedicatedCounters) self.actionLogic.triggered.connect(self.showLogicAnalyzer) self.currentTab = self.tabDict.at( min(len(self.tabDict)-1, self.config.get('MainWindow.currentIndex',0) ) ) self.tabWidget.setCurrentIndex( self.config.get('MainWindow.currentIndex',0) ) self.currentTab.activate() if hasattr( self.currentTab, 'stateChanged' ): self.currentTab.stateChanged.connect( self.todoList.onStateChanged ) if 'MainWindow.State' in self.config: self.parent.restoreState(self.config['MainWindow.State']) self.initMenu() self.actionResume.setEnabled(False) if 'MainWindow.pos' in self.config: self.move(self.config['MainWindow.pos']) if 'MainWindow.size' in self.config: self.resize(self.config['MainWindow.size']) if 'MainWindow.isMaximized' in self.config: if self.config['MainWindow.isMaximized']: self.showMaximized() else: self.showMaximized() self.dedicatedCountersWindow = DedicatedCounters(self.config, self.dbConnection, self.pulser, self.globalVariablesUi, self.shutterUi,self.ExternalParametersUi.callWhenDoneAdjusting ) self.dedicatedCountersWindow.setupUi(self.dedicatedCountersWindow) self.logicAnalyzerWindow = LogicAnalyzer(self.config, self.pulser, self.channelNameData ) self.logicAnalyzerWindow.setupUi(self.logicAnalyzerWindow) if self.voltagesEnabled: try: self.voltageControlWindow = VoltageControl(self.config, self.globalVariablesUi.globalDict, self.dac) self.voltageControlWindow.setupUi(self.voltageControlWindow) self.voltageControlWindow.globalAdjustUi.outputChannelsChanged.connect( partial(self.scanExperiment.updateScanTarget, 'Voltage') ) self.voltageControlWindow.localAdjustUi.outputChannelsChanged.connect( partial(self.scanExperiment.updateScanTarget, 'Voltage Local Adjust') ) self.scanExperiment.updateScanTarget('Voltage', self.voltageControlWindow.globalAdjustUi.outputChannels()) self.scanExperiment.updateScanTarget('Voltage Local Adjust', self.voltageControlWindow.localAdjustUi.outputChannels()) except MyException.MissingFile as e: self.voltageControlWindow = None self.actionVoltageControl.setDisabled( True ) logger.warning("Missing file - voltage subsystem disabled: {0}".format(str(e))) if self.voltageControlWindow: self.tabDict["Scan"].ppStartSignal.connect( self.voltageControlWindow.synchronize ) # upload shuttling data before running pule program self.dedicatedCountersWindow.autoLoad.setVoltageControl( self.voltageControlWindow ) self.setWindowTitle("Experimental Control ({0})".format(self.project) ) QtCore.QTimer.singleShot(60000, self.onCommitConfig ) traceFilename, _ = DataDirectory.DataDirectory().sequencefile("Trace.log") LoggingSetup.setTraceFilename( traceFilename ) errorFilename, _ = DataDirectory.DataDirectory().sequencefile("Error.log") LoggingSetup.setErrorFilename( errorFilename ) # connect signals and slots for todolist and auto resume for name, widget in self.tabDict.items(): if hasattr(widget,'onContinue'): self.dedicatedCountersWindow.autoLoad.ionReappeared.connect( widget.onContinue ) # add PushDestinations for widget in self.tabDict.values(): if hasattr(widget, 'addPushDestination'): widget.addPushDestination( 'External', self.ExternalParametersUi ) # initialize ScriptingUi self.scriptingWindow = ScriptingUi(self) self.scriptingWindow.setupUi(self.scriptingWindow) # this is redundant in __init__ but this resolves issues with user-defined functions that reference NamedTraces localpath = getProject().configDir+'/UserFunctions/' for filename in Path(localpath.replace('\\','/')).glob('**/*.py'): try: importlib.machinery.SourceFileLoader("CustomFunctions", str(filename).replace('\\','/')).load_module() except SyntaxError as e: SyntaxError('Failed to load {0}'.format(str(filename))) # initialize NamedTraceUi self.userFunctionsEditor = UserFunctionsEditor(self, self.globalVariablesUi.globalDict) self.userFunctionsEditor.setupUi(self.userFunctionsEditor) # initialize StashButton self.actionStash.triggered.connect(self.onStash) self.actionResume.triggered.connect(self.onResume) self.stashButton = StashButtonControl(self.actionResume) self.stashButton.resume.connect(self.onResume) def instantiateParametersUi(self, pulser, windowName, configName, config, globalDict): ui = PulserParameterUi(pulser, config, configName, globalDict) ui.setupUi() uiDock = QtWidgets.QDockWidget(windowName) uiDock.setWidget(ui) uiDock.setObjectName(windowName) self.addDockWidget(QtCore.Qt.RightDockWidgetArea, uiDock) self.tabDict['Scan'].NeedsDDSRewrite.connect(ui.onWriteAll) return ui, uiDock def instantiateDDSUi(self, pulser, windowName, configName, config, globalDict): ui = DDSUi.DDSUi(pulser, config, configName, globalDict) ui.setupUi(ui) uiDock = QtWidgets.QDockWidget(windowName) uiDock.setWidget(ui) uiDock.setObjectName(windowName) self.addDockWidget(QtCore.Qt.RightDockWidgetArea, uiDock) self.globalVariablesUi.valueChanged.connect(ui.evaluate) pulser.ppActiveChanged.connect(ui.setDisabled) self.tabDict['Scan'].NeedsDDSRewrite.connect(ui.onWriteAll) return ui, uiDock def instantiateDACUi(self, pulser, windowName, configName, config, globalDict): ui = DACUi(pulser, config, configName, globalDict) ui.setupUi(ui) uiDock = QtWidgets.QDockWidget(windowName) uiDock.setObjectName(windowName) uiDock.setWidget(ui) self.addDockWidget(QtCore.Qt.RightDockWidgetArea, uiDock) pulser.ppActiveChanged.connect(ui.setDisabled) self.tabDict['Scan'].NeedsDDSRewrite.connect(ui.onWriteAll) return ui, uiDock def instantiateShutterUi(self, pulser, windowName, configName, config, globalDict, nameDict, nameSignal): ui = ShutterUi.ShutterUi(pulser, configName, 'shutter', self.config, (nameDict, nameSignal), size=49) ui.setupUi(ui, True) uiDock = QtWidgets.QDockWidget(windowName) uiDock.setObjectName(windowName) uiDock.setWidget(ui) self.addDockWidget(QtCore.Qt.RightDockWidgetArea, uiDock) pulser.ppActiveChanged.connect(ui.setDisabled) logger.debug("ShutterUi representation:" + repr(ui)) return ui, uiDock def callWhenDoneAdjusting(self, callback): self.ExternalParametersUi.callWhenDoneAdjusting(callback) def onEnableConsole(self, state): self.consoleEnable = state==QtCore.Qt.Checked def onClearConsole(self): self.textEditConsole.clear() def onConsoleMaximumLinesChanged(self, maxlines): self.consoleMaximumLines = maxlines self.textEditConsole.document().setMaximumBlockCount(maxlines) def setLoggingLevel(self, index): self.loggingLevel = self.levelValueList[index] def showDedicatedCounters(self): self.dedicatedCountersWindow.show() self.dedicatedCountersWindow.setWindowState(QtCore.Qt.WindowActive) self.dedicatedCountersWindow.raise_() self.dedicatedCountersWindow.onStart() #Start displaying data immediately def showLogicAnalyzer(self): self.logicAnalyzerWindow.show() self.logicAnalyzerWindow.setWindowState(QtCore.Qt.WindowActive) self.logicAnalyzerWindow.raise_() def onVoltageControl(self): self.voltageControlWindow.show() self.voltageControlWindow.setWindowState(QtCore.Qt.WindowActive) self.voltageControlWindow.raise_() def onScripting(self): self.scriptingWindow.show() self.scriptingWindow.setWindowState(QtCore.Qt.WindowActive) self.scriptingWindow.raise_() def onUserFunctionsEditor(self): self.userFunctionsEditor.show() self.userFunctionsEditor.setWindowState(QtCore.Qt.WindowActive) self.userFunctionsEditor.raise_() def onAWG(self, displayName): awgUi = self.AWGUiDict[displayName] awgUi.show() awgUi.setWindowState(QtCore.Qt.WindowActive) awgUi.raise_() def onMeasurementLog(self): self.measurementLog.show() self.measurementLog.setWindowState(QtCore.Qt.WindowActive) self.measurementLog.raise_() def onClear(self): self.currentTab.onClear() def onSaveGUI(self, _): logger = logging.getLogger(__name__) self.currentTab.onSave() filename, _ = DataDirectory.DataDirectory().sequencefile("configuration.db") logger.info( "Saving config to "+filename ) self.saveConfig() self.config.saveConfig(filename) def onSaveGUIYaml(self, _): self.currentTab.onSave() logger.info("Saving config") yamlfilename, _ = DataDirectory.DataDirectory().sequencefile("configuration.yaml") self.saveConfig() self.config.saveConfig(yamlfile=yamlfilename) def onCommitConfig(self): logger = logging.getLogger(__name__) self.currentTab.onSave() logger.debug( "Committing config" ) self.saveConfig() self.config.commitToDatabase() QtCore.QTimer.singleShot(60000, self.onCommitConfig ) def onStart(self, checked=False, globalOverrides=list()): self.currentTab.onStart(globalOverrides) def onStash(self): if hasattr(self.currentTab, 'onStash'): self.currentTab.onStash() def onStashChanged(self, stash): self.actionResume.setEnabled(len(stash)>0) self.stashButton.onStashChanged(stash) def onResume(self, index=-1): if hasattr(self.currentTab, 'onResume'): self.currentTab.onResume(index) def onPause(self): self.currentTab.onPause() def onStop(self): self.currentTab.onStop() def onAbort(self): self.currentTab.onStop(reason='aborted') def onContinue(self): if hasattr(self.currentTab,'onContinue'): self.currentTab.onStop() else: self.statusbar.showMessage("continue not implemented") def onReload(self): logger = logging.getLogger(__name__) logger.debug( "OnReload" ) self.currentTab.onReload() def switchTab(self, name): self.tabWidget.setCurrentWidget( self.tabDict[name] ) self.onCurrentChanged(self.tabDict.index(name)) # this gets called later, but we need it to run now in order to switch scans from the todolist def onCurrentChanged(self, index): if self.tabDict.at(index)!=self.currentTab: self.currentTab.deactivate() if hasattr( self.currentTab, 'stateChanged' ): try: self.currentTab.stateChanged.disconnect() except TypeError: pass self.currentTab = self.tabDict.at(index) self.currentTab.activate() if hasattr( self.currentTab, 'stateChanged' ): self.currentTab.stateChanged.connect( self.todoList.onStateChanged ) self.initMenu() self.actionResume.setEnabled(self.currentTab.stashSize()) def initMenu(self): """setup print and view menus""" #view menu self.menuView.clear() if hasattr(self.currentTab,'viewActions'): self.menuView.addActions(self.currentTab.viewActions()) dockList = self.findChildren(QtWidgets.QDockWidget) for dock in dockList: self.menuView.addAction(dock.toggleViewAction()) #print menu self.menuPrint.clear() if hasattr(self.currentTab,'printTargets'): for plot in self.currentTab.printTargets(): action = self.menuPrint.addAction( plot ) action.triggered.connect( partial(self.onPrint, plot )) self.menuPrint.addSeparator() action = self.menuPrint.addAction("Print Preferences") action.triggered.connect(self.preferencesUiDock.show) action.triggered.connect(self.preferencesUiDock.raise_) def onPulses(self): self.pulseProgramDialog.show() self.pulseProgramDialog.setWindowState(QtCore.Qt.WindowActive) self.pulseProgramDialog.raise_() if hasattr(self.currentTab,'experimentName'): self.pulseProgramDialog.setCurrentTab(self.currentTab.experimentName) def onClose(self): self.parent.close() def onMessageWrite(self,message,level=logging.DEBUG): if self.consoleEnable and level>= self.loggingLevel: cursor = self.textEditConsole.textCursor() cursor.movePosition(QtGui.QTextCursor.End) if level < logging.ERROR: self.textEditConsole.setTextColor(QtCore.Qt.black) else: self.textEditConsole.setTextColor(QtCore.Qt.red) cursor.insertText(message) self.textEditConsole.setTextCursor(cursor) self.textEditConsole.ensureCursorVisible() def closeEvent(self,e): logger = logging.getLogger("") logger.debug( "Saving Configuration" ) self.saveConfig() self.config.commitToDatabase() for tab in self.tabDict.values(): tab.onClose() self.currentTab.deactivate() self.pulseProgramDialog.done(0) self.ExternalParametersSelectionUi.onClose() self.dedicatedCountersWindow.close() self.pulseProgramDialog.onClose() self.scriptingWindow.onClose() self.userFunctionsEditor.onClose() self.logicAnalyzerWindow.close() self.measurementLog.close() if self.voltagesEnabled: self.voltageControlWindow.close() for awgUi in self.AWGUiDict.values(): awgUi.close() numTempAreas = len(self.scanExperiment.area.tempAreas) for i in range(numTempAreas): if len(self.scanExperiment.area.tempAreas) > 0: self.scanExperiment.area.tempAreas[0].win.close() # close auxiliary pulsers #map(lambda x: x.shutdown(), self.auxiliaryPulsers) for p in self.auxiliaryPulsers: p.shutdown() def saveConfig(self): self.config['MainWindow.State'] = self.parent.saveState() for tab in self.tabDict.values(): tab.saveConfig() self.config['MainWindow.currentIndex'] = self.tabWidget.currentIndex() self.config['MainWindow.pos'] = self.pos() self.config['MainWindow.size'] = self.size() self.config['MainWindow.isMaximized'] = self.isMaximized() self.config['Settings.loggingLevel'] = self.loggingLevel self.config['Settings.consoleMaximumLinesNew'] = self.consoleMaximumLines self.config['Settings.ShutterNameDict'] = self.shutterNameDict self.config['SettingsTriggerNameDict'] = self.triggerNameDict self.config['Settings.consoleEnable'] = self.consoleEnable self.pulseProgramDialog.saveConfig() self.scriptingWindow.saveConfig() self.userFunctionsEditor.saveConfig() self.shutterUi.saveConfig() self.triggerUi.saveConfig() self.dedicatedCountersWindow.saveConfig() self.logicAnalyzerWindow.saveConfig() if self.voltagesEnabled: if self.voltageControlWindow: self.voltageControlWindow.saveConfig() self.ExternalParametersSelectionUi.saveConfig() self.globalVariablesUi.saveConfig() self.loggerUi.saveConfig() self.todoList.saveConfig() self.preferencesUi.saveConfig() self.measurementLog.saveConfig() self.valueHistoryUi.saveConfig() self.ExternalParametersUi.saveConfig() list(map(lambda x: x.saveConfig(), self.objectListToSaveContext)) # call saveConfig() for each element in the list for awgUi in self.AWGUiDict.values(): awgUi.saveConfig() def onProjectSelection(self): ui = ProjectInfoUi(self.project) ui.show() ui.exec_() def getCurrentTab(self): index = self.tabWidget.currentIndex() return self.tabDict.keyAt(index), self.tabDict.at(index) def setCurrentTab(self, name): self.onCurrentChanged(self.tabDict.index(name)) def onPrint(self, target): """Print action is triggered on 'target', which is a plot name""" if hasattr( self.currentTab, 'onPrint' ): printer = QtPrintSupport.QPrinter(mode=QtPrintSupport.QPrinter.ScreenResolution) if self.preferencesUi.preferences().printPreferences.doPrint: dialog = QtPrintSupport.QPrintDialog(printer, self) dialog.setWindowTitle("Print Document") if dialog.exec_() != QtWidgets.QDialog.Accepted: return printer.setResolution(self.preferencesUi.preferences().printPreferences.printResolution) pdfPrinter = QtPrintSupport.QPrinter() pdfPrinter.setOutputFormat(QtPrintSupport.QPrinter.PdfFormat) pdfPrinter.setOutputFileName(DataDirectory.DataDirectory().sequencefile(target+".pdf")[0]) self.currentTab.onPrint(target, printer, pdfPrinter, self.preferencesUi.preferences().printPreferences) def onShowDocumentation(self): url = "file://" + os.path.join(os.path.dirname(os.path.abspath(__file__)),"docs/_build/html/index.html") webbrowser.open(url, new=2) def show(self): """show ExperimentUi, and any of the other main windows which were previously visible""" super(ExperimentUi, self).show() # restore dock state of ScanExperiment. Because ScanExperiment is a child QMainWindow of ExperimentUi # (rather than an independent window), restoreState must be called after show() is called on the parent # widget in order to work properly. for tab in self.tabDict.values(): tabStateName = tab.experimentName+'.MainWindow.State' if tabStateName in self.config: tab.restoreState(self.config[tabStateName]) pulseProgramVisible = self.config.get(self.pulseProgramDialog.configname+'.isVisible', True) #pulse program defaults to visible if pulseProgramVisible: self.pulseProgramDialog.show() else: self.pulseProgramDialog.hide() scriptingWindowVisible = self.config.get(self.scriptingWindow.configname+'.isVisible', False) if scriptingWindowVisible: self.scriptingWindow.show() else: self.scriptingWindow.hide() userFunctionsEditorVisible = self.config.get(self.userFunctionsEditor.configname+'.isVisible', False) if userFunctionsEditorVisible: self.userFunctionsEditor.show() else: self.userFunctionsEditor.hide() if self.voltagesEnabled: voltageControlWindowVisible = getattr(self.voltageControlWindow.settings, 'isVisible', False) if voltageControlWindowVisible: self.voltageControlWindow.show() else: self.voltageControlWindow.hide() if self.AWGUiDict: for awgUi in self.AWGUiDict.values(): awgUiVisible = self.config.get(awgUi.configname+'.isVisible', False) if awgUiVisible: awgUi.show() else: awgUi.hide() self.setFocus(True) def setupFPGAs(self): """Setup all Opal Kelly FPGAs""" self.dac = DACController() #100 channel DAC board #determine name of FPGA used for Pulser, if any pulserName=None pulserSoftwareEnabled = self.project.isEnabled('software', 'Pulser') if pulserSoftwareEnabled: pulserHardware = next(iter(pulserSoftwareEnabled.values()))['hardware'] hardwareObjName, hardwareName = project.fromFullName(pulserHardware) if hardwareObjName=='Opal Kelly FPGA': pulserName=hardwareName self.settings = FPGASettings() #settings for pulser specifically #determine name of FPGA used for DAC, if any dacName=None voltageSoftwareEnabled = self.project.isEnabled('software', 'Voltages') if voltageSoftwareEnabled: voltageHardware = next(iter(voltageSoftwareEnabled.values()))['hardware'] hardwareObjName, hardwareName = project.fromFullName(voltageHardware) if hardwareObjName=='Opal Kelly FPGA': dacName=hardwareName self.OK_FPGA_Dict = self.pulser.listBoards() #list all connected Opal Kelly FPGA boards logger.info( "Opal Kelly Devices found: {0}".format({k:v.modelName for k,v in self.OK_FPGA_Dict.items()}) ) enabledFPGAs = self.project.isEnabled('hardware', 'Opal Kelly FPGA') #Dict of enabled FPGAs for FPGAName, FPGAConfig in enabledFPGAs.items(): FPGA = self.pulser if FPGAName==pulserName else (self.dac if FPGAName==dacName else OKBase()) deviceName=FPGAConfig.get('device') #The 'device' field of an FPGA should be the identifier of the FPGA. if not deviceName: logger.error("No FPGA specified: 'device' field missing in Opal Kelly FPGA: '{0}' config".format(FPGAName)) elif deviceName not in self.OK_FPGA_Dict: logger.error("FPGA device {0} specified in Opal Kelly FPGA: '{1}' config cannot be found".format(deviceName, FPGAName)) else: device=self.OK_FPGA_Dict[deviceName] FPGA.openBySerial(device.serial) bitFile=FPGAConfig.get('bitFile') checkFileValid(bitFile, 'bitfile', FPGAName) if FPGAName==pulserName: configFile = os.path.splitext(bitFile)[0] + '.xml' checkFileValid(configFile, 'config file', FPGAName) if FPGAConfig.get('uploadOnStartup'): FPGA.uploadBitfile(bitFile) logger.info("Uploaded file '{0}' to {1} (model {2}) in Opal Kelly FPGA: '{3}' config".format(bitFile, deviceName, device.modelName, FPGAName)) if FPGAName==pulserName: # check and make sure correct hardware is loaded try: FPGA.pulserConfiguration(configFile) except PulserHardwareException: logger.exception('PulserHardwareException occurred, likely because firmware must be uploaded. Please restart the program and upload the firmware.') if not self.project.exptConfig['showGui']: #force the GUI to be shown next time self.project.exptConfig['showGui'] = True with open(self.project.exptConfigFilename, 'w') as f: yaml.dump(self.project.exptConfig, f, default_flow_style=False) sys.exit("Please restart program and upload firmware (config GUI will be shown)") if FPGA==self.pulser: self.settings.deviceSerial = device.serial self.settings.deviceDescription = device.identifier self.settings.deviceInfo = device pulserHardwareId = self.pulser.hardwareConfigurationId() if pulserHardwareId: logger.info("Pulser Configuration {0:x}".format(pulserHardwareId)) else: logger.error("No pulser available") def instantiateAuxiliaryPulsers(self): self.auxiliaryPulsers = list() for FPGAName, FPGAConfig in self.project.isEnabled('hardware', 'Auxiliary Pulser').items(): FPGA = PulserHardware() deviceName=FPGAConfig.get('device') #The 'device' field of an FPGA should be the identifier of the FPGA. if not deviceName: logger.error("No FPGA specified: 'device' field missing in Auxiliary Opal Kelly FPGA: '{0}' config".format(FPGAName)) elif deviceName not in self.OK_FPGA_Dict: logger.error("FPGA device {0} specified in Auxiliary Opal Kelly FPGA: '{1}' config cannot be found".format(deviceName, FPGAName)) else: device=self.OK_FPGA_Dict[deviceName] FPGA.openBySerial(device.serial) bitFile=FPGAConfig.get('bitFile') checkFileValid(bitFile, 'bitfile', FPGAName) configFile = os.path.splitext(bitFile)[0] + '.xml' checkFileValid(configFile, 'config file', FPGAName) if FPGAConfig.get('uploadOnStartup'): FPGA.uploadBitfile(bitFile) logger.info("Uploaded file '{0}' to {1} (model {2}) in Opal Kelly FPGA: '{3}' config".format(bitFile, deviceName, device.modelName, FPGAName)) FPGA.pulserConfiguration(configFile) pulserHardwareId = self.pulser.hardwareConfigurationId() if pulserHardwareId: logger.info("Auxiliary Pulser {1} Configuration {0:x}".format(pulserHardwareId, FPGAName)) else: logger.error("No pulser available") if FPGAConfig.get('PulserParameters'): ui, _ = self.instantiateParametersUi(FPGA, "{0} Pulser Config".format(FPGAName), "{0}.PulserParameterUi".format(FPGAName), self.config, self.globalVariablesUi.globalDict) self.objectListToSaveContext.append(ui) if FPGAConfig.get('DDS'): ui, _ = self.instantiateDDSUi(FPGA, "{0} DDS".format(FPGAName), "{0}.DDSUi".format(FPGAName), self.config, self.globalVariablesUi.globalDict) self.objectListToSaveContext.append(ui) if FPGAConfig.get('DAC'): ui, _ = self.instantiateDACUi(FPGA, "{0} DAC".format(FPGAName), "{0}.dacUi".format(FPGAName), self.config, self.globalVariablesUi.globalDict) self.objectListToSaveContext.append(ui) if FPGAConfig.get('Shutters'): ui, _ = self.instantiateShutterUi(FPGA, "{0} Shutter".format(FPGAName), "{0}.ShutterUi".format(FPGAName), self.config, self.globalVariablesUi.globalDict, None, None) self.objectListToSaveContext.append(ui) self.auxiliaryPulsers.append(FPGA)
class SelectionUi(SelectionForm, SelectionBase): outputChannelsChanged = QtCore.pyqtSignal(object) inputChannelsChanged = QtCore.pyqtSignal(object) def __init__(self, config, globalDict, classdict, instancename="ExternalParameterSelection.ParametersSequence", parent=None): SelectionBase.__init__(self, parent) SelectionForm.__init__(self) self.config = config self.instancename = instancename self.parameters = self.config.get(self.instancename, SequenceDict()) self.enabledParametersObjects = SequenceDict() self.classdict = classdict self.globalDict = globalDict def setupUi(self, MainWindow): logger = logging.getLogger(__name__) SelectionForm.setupUi(self, MainWindow) self.parameterTableModel = ExternalParameterTableModel( self.parameters, self.classdict ) self.parameterTableModel.enableChanged.connect( self.onEnableChanged ) self.tableView.setModel( self.parameterTableModel ) self.tableView.resizeColumnsToContents() self.comboBoxDelegate = ComboBoxDelegate() self.tableView.setItemDelegateForColumn(3, self.comboBoxDelegate) self.tableView.setItemDelegateForColumn(2, self.comboBoxDelegate) self.tableView.horizontalHeader().setStretchLastSection(True) self.filter = KeyListFilter( [QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown] ) self.filter.keyPressed.connect( self.onReorder ) self.tableView.installEventFilter(self.filter) self.classComboBox.addItems(sorted(self.classdict.keys())) self.classComboBox.currentIndexChanged[str].connect(self.getInstrumentSuggestions) self.addParameterButton.clicked.connect( self.onAddParameter ) self.removeParameterButton.clicked.connect( self.onRemoveParameter ) self.refreshInstrumentComboBox.clicked.connect(self.getInstrumentSuggestions) for parameter in list(self.parameters.values()): if parameter.enabled: try: self.enableInstrument(parameter) except Exception as e: logger.warning("{0} while enabling instrument {1}".format(e, parameter.name)) parameter.enabled = False self.enabledParametersObjects.sortToMatch( list(self.parameters.keys()) ) self.emitSelectionChanged() self.tableView.selectionModel().currentChanged.connect( self.onActiveInstrumentChanged ) def outputChannels(self): self._outputChannels = dict(itertools.chain(*[p.outputChannelList() for p in self.enabledParametersObjects.values()])) return self._outputChannels def inputChannels(self): self._inputChannels = dict(itertools.chain(*[p.inputChannelList() for p in self.enabledParametersObjects.values()])) return self._inputChannels def emitSelectionChanged(self): self.outputChannelsChanged.emit( self.outputChannels() ) self.inputChannelsChanged.emit( self.inputChannels() ) def getInstrumentSuggestions(self, className=None): className = str(className) if className else self.classComboBox.currentText() myclass = self.classdict[className] if hasattr(myclass, 'connectedInstruments'): updateComboBoxItems(self.instrumentComboBox, sorted(myclass.connectedInstruments())) self.refreshInstrumentComboBox.setEnabled(True) else: self.instrumentComboBox.clear() self.refreshInstrumentComboBox.setEnabled(False) def onReorder(self, key): if key in [QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown]: indexes = self.tableView.selectedIndexes() up = key==QtCore.Qt.Key_PageUp delta = -1 if up else 1 rows = sorted(unique([ i.row() for i in indexes ]), reverse=not up) if self.parameterTableModel.moveRow( rows, up=up ): selectionModel = self.tableView.selectionModel() selectionModel.clearSelection() for index in indexes: selectionModel.select( self.parameterTableModel.createIndex(index.row()+delta, index.column()), QtCore.QItemSelectionModel.Select ) self.enabledParametersObjects.sortToMatch( list(self.parameters.keys()) ) self.emitSelectionChanged() def onEnableChanged(self, name): logger = logging.getLogger(__name__) parameter = self.parameters[name] if parameter.enabled: try: self.enableInstrument(parameter) except Exception as e: logger.exception( "{0} while enabling instrument {1}".format(e, name)) parameter.enabled = False self.parameterTableModel.setParameterDict( self.parameters ) else: self.disableInstrument(name) def onAddParameter(self): parameter = Parameter() parameter.instrument = str(self.instrumentComboBox.currentText()) parameter.className = str(self.classComboBox.currentText()) parameter.name = str(self.nameEdit.currentText()) if parameter.name not in self.parameters: self.parameters[parameter.name] = parameter self.parameterTableModel.setParameterDict( self.parameters ) self.tableView.resizeColumnsToContents() self.tableView.horizontalHeader().setStretchLastSection(True) def onRemoveParameter(self): for index in sorted(unique([ i.row() for i in self.tableView.selectedIndexes() ]), reverse=True): parameter = self.parameters.at(index) parameter.enabled=False self.disableInstrument(parameter.name) self.parameters.pop( parameter.name ) self.parameterTableModel.setParameterDict( self.parameters ) def enableInstrument(self, parameter): if parameter.name not in self.enabledParametersObjects: logger = logging.getLogger(__name__) instance = self.classdict[parameter.className](parameter.name, parameter.settings, self.globalDict, parameter.instrument) self.enabledParametersObjects[parameter.name] = instance self.enabledParametersObjects.sortToMatch( list(self.parameters.keys()) ) self.emitSelectionChanged() self.parameterTableModel.setParameterDict( self.parameters ) logger.info("Enabled Instrument {0} as {1}".format(parameter.className, parameter.name)) def disableInstrument(self, name): if name in self.enabledParametersObjects: logger = logging.getLogger(__name__) instance = self.enabledParametersObjects.pop( name ) instance.close() self.enabledParametersObjects.sortToMatch( list(self.parameters.keys()) ) self.emitSelectionChanged() parameter = self.parameters[name] logger.info("Disabled Instrument {0} as {1}".format(parameter.className, parameter.name)) def onActiveInstrumentChanged(self, modelIndex, modelIndex2 ): logger = logging.getLogger(__name__) logger.debug( "activeInstrumentChanged {0}".format( modelIndex.row() ) ) if self.parameters.at(modelIndex.row()).enabled: self.treeWidget.setParameters( self.enabledParametersObjects[self.parameters.at(modelIndex.row()).name].parameter ) def saveConfig(self): self.config[self.instancename] = self.parameters def onClose(self): for inst in list(self.enabledParametersObjects.values()): inst.close()
class FitFunctionBase(object, metaclass=FitFunctionMeta): expression = Expression() name = 'None' parameterNames = list() def __init__(self): numParameters = len(self.parameterNames) self.epsfcn = 0.0 self.parameters = [0] * numParameters self.startParameters = [1] * numParameters self.startParameterExpressions = None # will be initialized by FitUiTableModel if values are available self.parameterEnabled = [True] * numParameters self.parametersConfidence = [None] * numParameters self.units = None self.results = SequenceDict({'RMSres': ResultRecord(name='RMSres')}) self.useSmartStartValues = False self.hasSmartStart = not hasattr(self.smartStartValues, 'isNative') self.parametersUpdated = Observable() self.parameterBounds = [[None, None] for _ in range(numParameters)] self.parameterBoundsExpressions = None self.useErrorBars = True def __setstate__(self, state): state.pop('parameterNames', None) state.pop('cov_x', None) state.pop('infodict', None) state.pop('laguerreCacheEta', None) state.pop('laguerreTable', None) state.pop('pnCacheBeta', None) state.pop('pnTable', None) self.__dict__ = state self.__dict__.setdefault('useSmartStartValues', False) self.__dict__.setdefault('startParameterExpressions', None) self.__dict__.setdefault('parameterBounds', [[None, None] for _ in range(len(self.parameterNames))]) self.__dict__.setdefault('parameterBoundsExpressions', None) self.__dict__.setdefault('useErrorBars', True) self.hasSmartStart = not hasattr(self.smartStartValues, 'isNative') def allFitParameters(self, p): """return a list where the disabled parameters are added to the enabled parameters given in p""" pindex = 0 params = list() for index, enabled in enumerate(self.parameterEnabled): if enabled: params.append(p[pindex]) pindex += 1 else: params.append(float(self.startParameters[index])) return params @staticmethod def coercedValue(val, bounds): if bounds[1] is not None and val >= bounds[1]: val = float(0.95 * bounds[1] + 0.05 * bounds[0] if bounds[0] is not None else bounds[1] - 0.01) if bounds[0] is not None and val <= bounds[0]: val = float(0.95 * bounds[0] + 0.05 * bounds[1] if bounds[1] is not None else bounds[0] + 0.01) return val def enabledStartParameters(self, parameters=None, bounded=False): """return a list of only the enabled start parameters""" if parameters is None: parameters = self.startParameters params = list() if bounded: for enabled, param, bounds in zip(self.parameterEnabled, parameters, self.parameterBounds): if enabled: params.append(self.coercedValue(float(param), bounds)) else: for enabled, param in zip(self.parameterEnabled, parameters): if enabled: params.append(float(param)) return params def enabledFitParameters(self, parameters=None): """return a list of only the enabled fit parameters""" if parameters is None: parameters = self.parameters params = list() for enabled, param in zip(self.parameterEnabled, parameters): if enabled: params.append(float(param)) return params def enabledParameterNames(self): """return a list of only the enabled fit parameters""" params = list() for enabled, param in zip(self.parameterEnabled, self.parameterNames): if enabled: params.append(param) return params def setEnabledFitParameters(self, parameters): """set the fitted parameters if enabled""" pindex = 0 for index, enabled in enumerate(self.parameterEnabled): if enabled: self.parameters[index] = parameters[pindex] pindex += 1 else: self.parameters[index] = float(self.startParameters[index]) def setEnabledConfidenceParameters(self, confidence): """set the parameter confidence values for the enabled parameters""" pindex = 0 for index, enabled in enumerate(self.parameterEnabled): if enabled: self.parametersConfidence[index] = confidence[pindex] pindex += 1 else: self.parametersConfidence[index] = None @native def smartStartValues(self, x, y, parameters, enabled): return None def enabledSmartStartValues(self, x, y, parameters): smartParameters = self.smartStartValues(x, y, parameters, self.parameterEnabled) return [ smartparam if enabled else param for enabled, param, smartparam in zip(self.parameterEnabled, parameters, smartParameters) ] if smartParameters is not None else None def evaluate(self, globalDict): myReplacementDict = self.replacementDict() if globalDict is not None: myReplacementDict.update(globalDict) if self.startParameterExpressions is not None: self.startParameters = [ param if expr is None else self.expression.evaluateAsMagnitude( expr, myReplacementDict) for param, expr in zip( self.startParameters, self.startParameterExpressions) ] if self.parameterBoundsExpressions is not None: self.parameterBounds = [[ bound[0] if expr[0] is None else self.expression.evaluateAsMagnitude( expr[0], myReplacementDict), bound[1] if expr[1] is None else self.expression.evaluateAsMagnitude(expr[0], myReplacementDict) ] for bound, expr in zip(self.parameterBounds, self.parameterBoundsExpressions)] def enabledBounds(self): result = [[ float(bounds[0]) if bounds[0] is not None else None, float(bounds[1]) if bounds[1] is not None else None ] for enabled, bounds in zip(self.parameterEnabled, self.parameterBounds) if enabled] enabled = any((any(bounds) for bounds in result)) return result if enabled else None def leastsq(self, x, y, parameters=None, sigma=None): logger = logging.getLogger(__name__) # Ensure all values of sigma or non zero by replacing with the minimum nonzero value if sigma is not None and self.useErrorBars: nonzerosigma = sigma[sigma > 0] sigma[sigma == 0] = numpy.min( nonzerosigma) if len(nonzerosigma) > 0 else 1.0 else: sigma = None if parameters is None: parameters = [float(param) for param in self.startParameters] if self.useSmartStartValues: smartParameters = self.smartStartValues(x, y, parameters, self.parameterEnabled) if smartParameters is not None: parameters = [ smartparam if enabled else param for enabled, param, smartparam in zip( self.parameterEnabled, parameters, smartParameters) ] myEnabledBounds = self.enabledBounds() if myEnabledBounds: enabledOnlyParameters, cov_x, infodict, self.mesg, self.ier = leastsqbound( self.residuals, self.enabledStartParameters(parameters, bounded=True), args=(y, x, sigma), epsfcn=self.epsfcn, full_output=True, bounds=myEnabledBounds) else: enabledOnlyParameters, cov_x, infodict, self.mesg, self.ier = leastsq( self.residuals, self.enabledStartParameters(parameters), args=(y, x, sigma), epsfcn=self.epsfcn, full_output=True) self.setEnabledFitParameters(enabledOnlyParameters) self.update(self.parameters) logger.info("chisq {0}".format(sum(infodict["fvec"] * infodict["fvec"]))) # calculate final chi square self.chisq = sum(infodict["fvec"] * infodict["fvec"]) self.dof = max(len(x) - len(parameters), 1) RMSres = Q(sqrt(self.chisq / self.dof)) RMSres.significantDigits = 3 self.results['RMSres'].value = RMSres # chisq, sqrt(chisq/dof) agrees with gnuplot logger.info("success {0} {1}".format(self.ier, self.mesg)) logger.info("Converged with chi squared {0}".format(self.chisq)) logger.info("degrees of freedom, dof {0}".format(self.dof)) logger.info( "RMS of residuals (i.e. sqrt(chisq/dof)) {0}".format(RMSres)) logger.info("Reduced chisq (i.e. variance of residuals) {0}".format( self.chisq / self.dof)) # uncertainties are calculated as per gnuplot, "fixing" the result # for non unit values of the reduced chisq. # values at min match gnuplot enabledParameterNames = self.enabledParameterNames() if cov_x is not None: enabledOnlyParametersConfidence = numpy.sqrt( numpy.diagonal(cov_x)) * sqrt(self.chisq / self.dof) self.setEnabledConfidenceParameters( enabledOnlyParametersConfidence) logger.info("Fitted parameters at minimum, with 68% C.I.:") for i, pmin in enumerate(enabledOnlyParameters): logger.info( "%2i %-10s %12f +/- %10f" % (i, enabledParameterNames[i], pmin, sqrt(max(cov_x[i, i], 0)) * sqrt(self.chisq / self.dof))) logger.info("Correlation matrix") # correlation matrix close to gnuplot messagelist = [" "] for i in range(len(enabledOnlyParameters)): messagelist.append("%-10s" % (enabledParameterNames[i], )) logger.info(" ".join(messagelist)) messagelist = [] for i in range(len(enabledOnlyParameters)): messagelist.append("%10s" % enabledParameterNames[i]) for j in range(i + 1): messagelist.append( "%10f" % (cov_x[i, j] / sqrt(abs(cov_x[i, i] * cov_x[j, j])), )) logger.info(" ".join(messagelist)) #----------------------------------------------- else: self.parametersConfidence = [None] * len(self.parametersConfidence) return self.parameters def __str__(self): return "; ".join([ ", ".join([self.name, self.functionString] + [ "{0}={1}".format(name, value) for name, value in zip(self.parameterNames, self.parameters) ]) ]) def setConstant(self, name, value): setattr(self, name, value) def update(self, parameters=None): self.parametersUpdated.fire(values=self.replacementDict()) def toXmlElement(self, parent): myroot = ElementTree.SubElement(parent, 'FitFunction', { 'name': self.name, 'functionString': self.functionString }) for name, value, confidence, enabled, startExpression, bounds, boundsexpression in zip_longest( self.parameterNames, self.parameters, self.parametersConfidence, self.parameterEnabled, self.startParameterExpressions, self.parameterBounds, self.parameterBoundsExpressions): e = ElementTree.SubElement( myroot, 'Parameter', { 'name': name, 'confidence': repr(confidence), 'enabled': str(enabled), 'startExpression': str(startExpression), 'bounds': ",".join(map(str, bounds)), 'boundsExpression': ",".join(map(str, boundsexpression)) }) e.text = str(value) for result in list(self.results.values()): e = ElementTree.SubElement(myroot, 'Result', { 'name': result.name, 'definition': str(result.definition) }) e.text = str(result.value) return myroot def toHdf5(self, group): fitfunction_group = group.require_group('fitfunction') fitfunction_group.attrs['name'] = self.name fitfunction_group.attrs['functionString'] = self.functionString parameter_group = fitfunction_group.require_group('parameters') for index, (name, value, confidence, enabled, startExpression, bounds, boundsexpression) in enumerate( zip_longest(self.parameterNames, self.parameters, self.parametersConfidence, self.parameterEnabled, self.startParameterExpressions, self.parameterBounds, self.parameterBoundsExpressions)): g = parameter_group.require_group(name) g.attrs['confidence'] = confidence g.attrs['enabled'] = enabled g.attrs['startExpression'] = str(startExpression) g.attrs['bounds'] = bounds g.attrs['boundsExpression'] = ",".join(map(str, boundsexpression)) g.attrs['value'] = value g.attrs['index'] = index results_group = fitfunction_group.require_group('results') for result in list(self.results.values()): g = results_group.requie_group(result.name) g.attrs['definition'] = str(result.definition) g.attrs['value'] = repr(result.value) def residuals(self, p, y, x, sigma): p = self.allFitParameters(p) if sigma is not None: return (y - self.functionEval(x, *p)) / sigma else: return y - self.functionEval(x, *p) def value(self, x, p=None): p = self.parameters if p is None else p return self.functionEval(x, *p) def replacementDict(self): replacement = dict(list(zip(self.parameterNames, self.parameters))) replacement.update( dict(((v.name, v.value) for v in list(self.results.values())))) return replacement
class VoltageGlobalAdjust(VoltageGlobalAdjustForm, VoltageGlobalAdjustBase ): updateOutput = QtCore.pyqtSignal(object, object) outputChannelsChanged = QtCore.pyqtSignal(object) _channelParams = {} def __init__(self, config, globalDict, parent=None): VoltageGlobalAdjustForm.__init__(self) VoltageGlobalAdjustBase.__init__(self, parent) self.config = config self.configname = 'VoltageGlobalAdjust.Settings' self.settings = self.config.get(self.configname, Settings()) self.globalAdjustDict = SequenceDict() self.myLabelList = list() self.myBoxList = list() self.historyCategory = 'VoltageGlobalAdjust' self.adjustHistoryName = None self.globalDict = globalDict self.adjustCache = self.config.get(self.configname+".cache", dict()) self.savedValue = defaultdict( lambda: None ) self.displayValueObservable = defaultdict( lambda: Observable() ) def setupUi(self, parent): VoltageGlobalAdjustForm.setupUi(self, parent) self.gainBox.setValue(self.settings.gain) self.gainBox.valueChanged.connect( self.onGainChanged ) self.tableModel = VoltageGlobalAdjustTableModel( self.globalAdjustDict, self.globalDict ) self.tableView.setModel( self.tableModel ) self.tableView.setSortingEnabled(True) # triggers sorting self.delegate = MagnitudeSpinBoxDelegate(self.globalDict) self.tableView.setItemDelegateForColumn(1, self.delegate) def onGainChanged(self, gain): self.settings.gain = gain self.updateOutput.emit(self.globalAdjustDict, self.settings.gain) def setupGlobalAdjust(self, name, adjustDict): if name!=self.adjustHistoryName: self.adjustCache[self.adjustHistoryName] = [v.data for v in list(self.globalAdjustDict.values())] self.settings.gainCache[self.adjustHistoryName] = self.settings.gain self.settings.gain = self.settings.gainCache.get( name, self.settings.gain ) if name in self.adjustCache: for data in self.adjustCache[name]: if data[0] in adjustDict: adjustDict[data[0]].data = data self.adjustHistoryName = name self.globalAdjustDict = adjustDict for name, adjust in self.globalAdjustDict.items(): try: adjust.valueChanged.connect(self.onValueChanged, QtCore.Qt.UniqueConnection) except: pass self.tableModel.setGlobalAdjust( adjustDict ) self.outputChannelsChanged.emit( self.outputChannels() ) self.gainBox.setValue(self.settings.gain) #self.updateOutput.emit(self.globalAdjustDict, self.settings.gain) def onValueChanged(self, name, value, string, origin): if origin=='recalculate': self.tableModel.valueRecalcualted(name) self.globalAdjustDict[name]._value = float(self.globalAdjustDict[name]._value) self.updateOutput.emit(self.globalAdjustDict, self.settings.gain) def saveConfig(self): self.config[self.configname] = self.settings self.adjustCache[self.adjustHistoryName] = [v.data for v in list(self.globalAdjustDict.values())] self.config[self.configname+".cache"] = self.adjustCache def setValue(self, channel, value): self.globalAdjustDict[channel].value = value return value def getValue(self, channel): return self.globalAdjustDict[channel].value def currentValue(self, channel): return self.globalAdjustDict[channel].value def saveValue(self, channel): self.savedValue[channel] = self.globalAdjustDict[channel].value def restoreValue(self, channel): if self.savedValue[channel] is not None: self.globalAdjustDict[channel].value = self.savedValue[channel] return True def strValue(self, channel): adjust = self.globalAdjustDict[channel] return adjust.string if adjust.hasDependency else None def setStrValue(self, channel, value): pass def outputChannels(self): self._outputChannels = dict(( (channelName, VoltageOutputChannel(self, None, channelName, self.globalDict, )) for channelName in self.globalAdjustDict.keys() )) return self._outputChannels
class FitFunctionBase(object, metaclass=FitFunctionMeta): expression = Expression() name = 'None' parameterNames = list() def __init__(self): numParameters = len(self.parameterNames) self.epsfcn=0.0 self.parameters = [0] * numParameters self.startParameters = [1] * numParameters self.startParameterExpressions = None # will be initialized by FitUiTableModel if values are available self.parameterEnabled = [True] * numParameters self.parametersConfidence = [None] * numParameters self.units = None self.results = SequenceDict({'RMSres': ResultRecord(name='RMSres')}) self.useSmartStartValues = False self.hasSmartStart = not hasattr(self.smartStartValues, 'isNative' ) self.parametersUpdated = Observable() self.parameterBounds = [[None, None] for _ in range(numParameters) ] self.parameterBoundsExpressions = None self.useErrorBars = True def __setstate__(self, state): state.pop('parameterNames', None ) state.pop('cov_x', None) state.pop('infodict', None) state.pop('laguerreCacheEta', None ) state.pop('laguerreTable', None) state.pop('pnCacheBeta', None ) state.pop('pnTable', None) self.__dict__ = state self.__dict__.setdefault( 'useSmartStartValues', False ) self.__dict__.setdefault( 'startParameterExpressions', None ) self.__dict__.setdefault( 'parameterBounds', [[None, None] for _ in range(len(self.parameterNames)) ] ) self.__dict__.setdefault( 'parameterBoundsExpressions', None) self.__dict__.setdefault( 'useErrorBars', True) self.hasSmartStart = not hasattr(self.smartStartValues, 'isNative' ) def allFitParameters(self, p): """return a list where the disabled parameters are added to the enabled parameters given in p""" pindex = 0 params = list() for index, enabled in enumerate(self.parameterEnabled): if enabled: params.append(p[pindex]) pindex += 1 else: params.append(float(self.startParameters[index])) return params @staticmethod def coercedValue( val, bounds ): if bounds[1] is not None and val>=bounds[1]: val = float(0.95*bounds[1]+0.05*bounds[0] if bounds[0] is not None else bounds[1]-0.01) if bounds[0] is not None and val<=bounds[0]: val = float(0.95*bounds[0]+0.05*bounds[1] if bounds[1] is not None else bounds[0]+0.01) return val def enabledStartParameters(self, parameters=None, bounded=False): """return a list of only the enabled start parameters""" if parameters is None: parameters = self.startParameters params = list() if bounded: for enabled, param, bounds in zip(self.parameterEnabled, parameters, self.parameterBounds): if enabled: params.append(self.coercedValue(float(param), bounds)) else: for enabled, param in zip(self.parameterEnabled, parameters): if enabled: params.append(float(param)) return params def enabledFitParameters(self, parameters=None): """return a list of only the enabled fit parameters""" if parameters is None: parameters = self.parameters params = list() for enabled, param in zip(self.parameterEnabled, parameters): if enabled: params.append(float(param)) return params def enabledParameterNames(self): """return a list of only the enabled fit parameters""" params = list() for enabled, param in zip(self.parameterEnabled, self.parameterNames): if enabled: params.append(param) return params def setEnabledFitParameters(self, parameters): """set the fitted parameters if enabled""" pindex = 0 for index, enabled in enumerate(self.parameterEnabled): if enabled: self.parameters[index] = parameters[pindex] pindex += 1 else: self.parameters[index] = float(self.startParameters[index]) def setEnabledConfidenceParameters(self, confidence): """set the parameter confidence values for the enabled parameters""" pindex = 0 for index, enabled in enumerate(self.parameterEnabled): if enabled: self.parametersConfidence[index] = confidence[pindex] pindex += 1 else: self.parametersConfidence[index] = None @native def smartStartValues(self, x, y, parameters, enabled): return None def enabledSmartStartValues(self, x, y, parameters): smartParameters = self.smartStartValues(x, y, parameters, self.parameterEnabled) return [ smartparam if enabled else param for enabled, param, smartparam in zip(self.parameterEnabled, parameters, smartParameters)] if smartParameters is not None else None def evaluate(self, globalDict ): myReplacementDict = self.replacementDict() if globalDict is not None: myReplacementDict.update( globalDict ) if self.startParameterExpressions is not None: self.startParameters = [param if expr is None else self.expression.evaluateAsMagnitude(expr, myReplacementDict ) for param, expr in zip(self.startParameters, self.startParameterExpressions)] if self.parameterBoundsExpressions is not None: self.parameterBounds = [[bound[0] if expr[0] is None else self.expression.evaluateAsMagnitude(expr[0], myReplacementDict), bound[1] if expr[1] is None else self.expression.evaluateAsMagnitude(expr[0], myReplacementDict)] for bound, expr in zip(self.parameterBounds, self.parameterBoundsExpressions)] def enabledBounds(self): result = [[float(bounds[0]) if bounds[0] is not None else None, float(bounds[1]) if bounds[1] is not None else None] for enabled, bounds in zip(self.parameterEnabled, self.parameterBounds) if enabled] enabled = any( (any(bounds) for bounds in result) ) return result if enabled else None def leastsq(self, x, y, parameters=None, sigma=None): logger = logging.getLogger(__name__) # Ensure all values of sigma or non zero by replacing with the minimum nonzero value if sigma is not None and self.useErrorBars: nonzerosigma = sigma[sigma>0] sigma[sigma==0] = numpy.min(nonzerosigma) if len(nonzerosigma)>0 else 1.0 else: sigma = None if parameters is None: parameters = [float(param) for param in self.startParameters] if self.useSmartStartValues: smartParameters = self.smartStartValues(x, y, parameters, self.parameterEnabled) if smartParameters is not None: parameters = [ smartparam if enabled else param for enabled, param, smartparam in zip(self.parameterEnabled, parameters, smartParameters)] myEnabledBounds = self.enabledBounds() if myEnabledBounds: enabledOnlyParameters, cov_x, infodict, self.mesg, self.ier = leastsqbound(self.residuals, self.enabledStartParameters(parameters, bounded=True), args=(y, x, sigma), epsfcn=self.epsfcn, full_output=True, bounds=myEnabledBounds) else: enabledOnlyParameters, cov_x, infodict, self.mesg, self.ier = leastsq(self.residuals, self.enabledStartParameters(parameters), args=(y, x, sigma), epsfcn=self.epsfcn, full_output=True) self.setEnabledFitParameters(enabledOnlyParameters) self.update(self.parameters) logger.info( "chisq {0}".format( sum(infodict["fvec"]*infodict["fvec"]) ) ) # calculate final chi square self.chisq=sum(infodict["fvec"]*infodict["fvec"]) self.dof = max( len(x)-len(parameters), 1) RMSres = Q(sqrt(self.chisq/self.dof)) RMSres.significantDigits = 3 self.results['RMSres'].value = RMSres # chisq, sqrt(chisq/dof) agrees with gnuplot logger.info( "success {0} {1}".format( self.ier, self.mesg ) ) logger.info( "Converged with chi squared {0}".format(self.chisq) ) logger.info( "degrees of freedom, dof {0}".format( self.dof ) ) logger.info( "RMS of residuals (i.e. sqrt(chisq/dof)) {0}".format( RMSres ) ) logger.info( "Reduced chisq (i.e. variance of residuals) {0}".format( self.chisq/self.dof ) ) # uncertainties are calculated as per gnuplot, "fixing" the result # for non unit values of the reduced chisq. # values at min match gnuplot enabledParameterNames = self.enabledParameterNames() if cov_x is not None: enabledOnlyParametersConfidence = numpy.sqrt(numpy.diagonal(cov_x))*sqrt(self.chisq/self.dof) self.setEnabledConfidenceParameters(enabledOnlyParametersConfidence) logger.info( "Fitted parameters at minimum, with 68% C.I.:" ) for i, pmin in enumerate(enabledOnlyParameters): logger.info( "%2i %-10s %12f +/- %10f"%(i, enabledParameterNames[i], pmin, sqrt(max(cov_x[i, i], 0))*sqrt(self.chisq/self.dof)) ) logger.info( "Correlation matrix" ) # correlation matrix close to gnuplot messagelist = [" "] for i in range(len(enabledOnlyParameters)): messagelist.append( "%-10s"%(enabledParameterNames[i],) ) logger.info( " ".join(messagelist)) messagelist = [] for i in range(len(enabledOnlyParameters)): messagelist.append( "%10s"%enabledParameterNames[i] ) for j in range(i+1): messagelist.append( "%10f"%(cov_x[i, j]/sqrt(abs(cov_x[i, i]*cov_x[j, j])),) ) logger.info( " ".join(messagelist)) #----------------------------------------------- else: self.parametersConfidence = [None]*len(self.parametersConfidence) return self.parameters def __str__(self): return "; ".join([", ".join([self.name, self.functionString] + [ "{0}={1}".format(name, value) for name, value in zip(self.parameterNames, self.parameters)])]) def setConstant(self, name, value): setattr(self, name, value) def update(self,parameters=None): self.parametersUpdated.fire( values=self.replacementDict() ) def toXmlElement(self, parent): myroot = ElementTree.SubElement(parent, 'FitFunction', {'name': self.name, 'functionString': self.functionString}) for name, value, confidence, enabled, startExpression, bounds, boundsexpression in zip_longest(self.parameterNames, self.parameters, self.parametersConfidence, self.parameterEnabled, self.startParameterExpressions, self.parameterBounds, self.parameterBoundsExpressions): e = ElementTree.SubElement( myroot, 'Parameter', {'name':name, 'confidence':repr(confidence), 'enabled': str(enabled), 'startExpression': str(startExpression), 'bounds': ",".join(map(str, bounds)), 'boundsExpression': ",".join(map(str, boundsexpression))}) e.text = str(value) for result in list(self.results.values()): e = ElementTree.SubElement( myroot, 'Result', {'name':result.name, 'definition':str(result.definition)}) e.text = str(result.value) return myroot def toHdf5(self, group): fitfunction_group = group.require_group('fitfunction') fitfunction_group.attrs['name'] = self.name fitfunction_group.attrs['functionString'] = self.functionString parameter_group = fitfunction_group.require_group('parameters') for index, (name, value, confidence, enabled, startExpression, bounds, boundsexpression) in enumerate(zip_longest(self.parameterNames, self.parameters, self.parametersConfidence, self.parameterEnabled, self.startParameterExpressions, self.parameterBounds, self.parameterBoundsExpressions)): g = parameter_group.require_group(name) g.attrs['confidence'] = confidence g.attrs['enabled'] = enabled g.attrs['startExpression'] = str(startExpression) g.attrs['bounds'] = bounds g.attrs['boundsExpression'] = ",".join(map(str, boundsexpression)) g.attrs['value'] = value g.attrs['index'] = index results_group = fitfunction_group.require_group('results') for result in list(self.results.values()): g = results_group.requie_group(result.name) g.attrs['definition'] = str(result.definition) g.attrs['value'] = repr(result.value) def residuals(self, p, y, x, sigma): p = self.allFitParameters(p) if sigma is not None: return (y-self.functionEval(x, *p))/sigma else: return y-self.functionEval(x, *p) def value(self,x,p=None): p = self.parameters if p is None else p return self.functionEval(x, *p ) def replacementDict(self): replacement = dict(list(zip(self.parameterNames, self.parameters))) replacement.update( dict( ( (v.name, v.value) for v in list(self.results.values()) ) ) ) return replacement