class ExamineVariablesDictionary(traits.HasTraits): """simple wrapper class for displaying the contents of the variables dictionary for a particular hardware action""" hdwA = traits.Instance(hardwareAction.hardwareAction.HardwareAction) displayList = traits.List(traits.Str) hardwareActionName = traits.Str def __init__(self, **traitsDict): super(ExamineVariablesDictionary, self).__init__(**traitsDict) def generateDisplayList(self): """return a new python lis that is ready for display """ dispList = [] for v in self.hdwA.variables: if v in self.hdwA.variablesReference: value = "%G" % self.hdwA.variablesReference[ v] # string formatted float else: logger.warning( "we are missing variable %s for hardware %s in variable definitions" % (v, self.hardwareActionName)) value = "?" # key not found could be we haven't loaded the dictionary or that the variable is missing in control dispList.append("%s = %s" % (v, value)) return dispList def updateDisplayList(self): """updates display list to be a list that contains the keys and values of variablesList in nice strings for user to see""" self.displayList = self.generateDisplayList() def _displayList_default(self): return self.generateDisplayList() def _hardwareActionName_default(self): return self.hdwA.hardwareActionName traits_view = traitsui.View( traitsui.VGroup( traitsui.Item("hardwareActionName", show_label=False, style="readonly"), traitsui.Item("displayList", editor=traitsui.ListEditor(style="readonly"), show_label=False)))
class OptionsDictionaryDialog(traits.HasTraits): """options dictionary dialog for editing the options of an image processor""" optionsList = traits.List(Option) traits_view = traitsui.View( traitsui.VGroup( traitsui.Item("optionsList", editor=traitsui.ListEditor(style="custom"), show_label=False, resizable=True)), buttons=[traitsui.OKButton, traitsui.CancelButton], kind="livemodal", width=0.15, height=0.5) def populateOptionsFromDictionary(self, optionsDict): """creates the options for GUI from an options dictionary""" for key, value in optionsDict.iteritems(): self.optionsList.append(createOption(key, value)) def getOptionsDictionaryFromList(self): """returns an options dictionary from the current state of the options list""" return collections.OrderedDict([(option.name, option.value) for option in self.optionsList])
class Fit(traits.HasTraits): name = traits.Str(desc="name of fit") function = traits.Str(desc="function we are fitting with all parameters") variablesList = traits.List(Parameter) calculatedParametersList = traits.List(CalculatedParameter) xs = None # will be a scipy array ys = None # will be a scipy array zs = None # will be a scipy array performFitButton = traits.Button("Perform Fit") getInitialParametersButton = traits.Button("Guess Initial Values") usePreviousFitValuesButton = traits.Button("Use Previous Fit") drawRequestButton = traits.Button("Draw Fit") setSizeButton = traits.Button("Set Initial Size") chooseVariablesButtons = traits.Button("choose logged variables") logAllVariables = traits.Bool(True) logLibrarianButton = traits.Button("librarian") logLastFitButton = traits.Button("log current fit") removeLastFitButton = traits.Button("remove last fit") autoFitBool = traits.Bool( False, desc= "Automatically perform this Fit with current settings whenever a new image is loaded" ) autoDrawBool = traits.Bool( True, desc= "Once a fit is complete update the drawing of the fit or draw the fit for the first time" ) autoGuessBool = traits.Bool(False, desc="Perform a new guess before fitting") autoSizeBool = traits.Bool( False, desc= "If TOF variable is read from latest XML and is equal to 0.11ms (or time set in Physics) then it will automatically update the physics sizex and sizey with the Sigma x and sigma y from the gaussian fit" ) autoPreviousBool = traits.Bool( False, desc= "Whenever a fit is completed replace the guess values with the calculated values (useful for increasing speed of the next fit)" ) logBool = traits.Bool( False, desc="Log the calculated and fitted values with a timestamp") logName = traits.String( desc="name of the scan - will be used in the folder name") logToNas = traits.Bool( True, desc="If true, log goes to Humphry-NAS instead of ursa") ## logDirectory = os.path.join("\\\\ursa","AQOGroupFolder","Experiment Humphry","Data","eagleLogs") logDirectory = os.path.join("G:", os.sep, "Experiment Humphry", "Data", "eagleLogs") logDirectoryNas = os.path.join("\\\\192.168.16.71", "Humphry", "Data", "eagleLogs") latestSequence = os.path.join("\\\\ursa", "AQOGroupFolder", "Experiment Humphry", "Experiment Control And Software", "currentSequence", "latestSequence.xml") conditionalFitBool = traits.Bool( False, desc= "If true, fit is only executed, if current sequence contains matching variable 'eagleID'" ) conditionalFitID = traits.Int(0) logFile = traits.File(desc="file path of logFile") logAnalyserBool = traits.Bool( False, desc="only use log analyser script when True") logAnalysers = [ ] #list containing full paths to each logAnalyser file to run logAnalyserDisplayString = traits.String( desc= "comma separated read only string that is a list of all logAnalyser python scripts to run. Use button to choose files" ) logAnalyserSelectButton = traits.Button("sel. analyser", image='@icons:function_node', style="toolbar") xmlLogVariables = [] imageInspectorReference = None #will be a reference to the image inspector fitting = traits.Bool(False) #true when performing fit fitted = traits.Bool( False) #true when current data displayed has been fitted fitSubSpace = traits.Bool(True) drawSubSpace = traits.Bool(True) startX = traits.Int(230) startY = traits.Int(230) endX = traits.Int(550) endY = traits.Int(430) fittingStatus = traits.Str() fitThread = None fitTimeLimit = traits.Float( 10.0, desc= "Time limit in seconds for fitting function. Only has an effect when fitTimeLimitBool is True" ) fitTimeLimitBool = traits.Bool( True, desc= "If True then fitting functions will be limited to time limit defined by fitTimeLimit " ) physics = traits.Instance( physicsProperties.physicsProperties.PhysicsProperties) #status strings notFittedForCurrentStatus = "Not Fitted for Current Image" fittedForCurrentImageStatus = "Fit Complete for Current Image" currentlyFittingStatus = "Currently Fitting..." failedFitStatus = "Failed to finish fit. See logger" timeExceededStatus = "Fit exceeded user time limit" lmfitModel = traits.Instance( lmfit.Model ) #reference to the lmfit model must be initialised in subclass mostRecentModelResult = None # updated to the most recent ModelResult object from lmfit when a fit thread is performed fitSubSpaceGroup = traitsui.VGroup( traitsui.HGroup( traitsui.Item("fitSubSpace", label="Fit Sub Space", resizable=True), traitsui.Item("drawSubSpace")), traitsui.VGroup(traitsui.HGroup( traitsui.Item("startX", resizable=True), traitsui.Item("startY", resizable=True)), traitsui.HGroup(traitsui.Item("endX", resizable=True), traitsui.Item("endY", resizable=True)), visible_when="fitSubSpace"), label="Fit Sub Space", show_border=True) generalGroup = traitsui.VGroup(traitsui.Item("name", label="Fit Name", style="readonly", resizable=True), traitsui.Item("function", label="Fit Function", style="readonly", resizable=True), fitSubSpaceGroup, label="Fit", show_border=True) variablesGroup = traitsui.VGroup(traitsui.Item( "variablesList", editor=traitsui.ListEditor(style="custom"), show_label=False, resizable=True), show_border=True, label="parameters") derivedGroup = traitsui.VGroup(traitsui.Item( "calculatedParametersList", editor=traitsui.ListEditor(style="custom"), show_label=False, resizable=True), show_border=True, label="derived values") buttons = traitsui.HGroup( traitsui.VGroup(traitsui.HGroup( traitsui.Item("autoFitBool", label="Auto fit?", resizable=True), traitsui.Item("performFitButton", show_label=False, resizable=True)), traitsui.HGroup( traitsui.Item("autoGuessBool", label="Auto guess?", resizable=True), traitsui.Item("getInitialParametersButton", show_label=False, resizable=True)), traitsui.HGroup( traitsui.Item("autoPreviousBool", label="Auto previous?", resizable=True), traitsui.Item("usePreviousFitValuesButton", show_label=False, resizable=True)), show_border=True), traitsui.VGroup(traitsui.HGroup( traitsui.Item("autoDrawBool", label="Auto draw?", resizable=True), traitsui.Item("drawRequestButton", show_label=False, resizable=True)), traitsui.HGroup( traitsui.Item("autoSizeBool", label="Auto size?", resizable=True), traitsui.Item("setSizeButton", show_label=False, resizable=True)), traitsui.HGroup( traitsui.Item("conditionalFitBool", label="Conditional Fit?", resizable=True), traitsui.Item("conditionalFitID", label="Fit ID", resizable=True)), show_border=True)) logGroup = traitsui.VGroup( traitsui.HGroup( traitsui.Item("logBool", resizable=True), traitsui.Item("logAllVariables", resizable=True), traitsui.Item("chooseVariablesButtons", show_label=False, resizable=True, enabled_when="not logAllVariables")), traitsui.HGroup(traitsui.Item("logName", resizable=True)), traitsui.HGroup(traitsui.Item("logToNas", resizable=True)), #changed traitsui.HGroup( traitsui.Item("removeLastFitButton", show_label=False, resizable=True), traitsui.Item("logLastFitButton", show_label=False, resizable=True)), traitsui.HGroup( traitsui.Item("logAnalyserBool", label="analyser?", resizable=True), traitsui.Item("logAnalyserDisplayString", show_label=False, style="readonly", resizable=True), traitsui.Item("logAnalyserSelectButton", show_label=False, resizable=True)), label="Logging", show_border=True) actionsGroup = traitsui.VGroup(traitsui.Item("fittingStatus", style="readonly", resizable=True), logGroup, buttons, label="Fit Actions", show_border=True) traits_view = traitsui.View(traitsui.VGroup(generalGroup, variablesGroup, derivedGroup, actionsGroup), kind="subpanel") def __init__(self, **traitsDict): super(Fit, self).__init__(**traitsDict) # self.startX = 0 # self.startY = 0 self.lmfitModel = lmfit.Model(self.fitFunc) # load config with open(configFile, 'r') as f: settings = json.load(f) if 'logAnalysers' in settings: self.logAnalysers = settings['logAnalysers'] self.logAnalyserDisplayString = str( [os.path.split(path)[1] for path in self.logAnalysers]) if 'logAnalyserBool' in settings: self.logAnalyserBool = settings['logAnalyserBool'] def _set_xs(self, xs): self.xs = xs def _set_ys(self, ys): self.ys = ys def _set_zs(self, zs): self.zs = zs def _fittingStatus_default(self): return self.notFittedForCurrentStatus def _getInitialValues(self): """returns ordered list of initial values from variables List """ return [_.initialValue for _ in self.variablesList] def _getParameters(self): """creates an lmfit parameters object based on the user input in variablesList """ return lmfit.Parameters( {_.name: _.parameter for _ in self.variablesList}) def _getCalculatedValues(self): """returns ordered list of fitted values from variables List """ return [_.calculatedValue for _ in self.variablesList] def _intelligentInitialValues(self): """If possible we can auto set the initial parameters to intelligent guesses user can always overwrite them """ self._setInitialValues(self._getIntelligentInitialValues()) def _get_subSpaceArrays(self): """returns the arrays of the selected sub space. If subspace is not activated then returns the full arrays""" if self.fitSubSpace: xs = self.xs[self.startX:self.endX] ys = self.ys[self.startY:self.endY] logger.info("xs array sliced length %s " % (xs.shape)) logger.info("ys array sliced length %s " % (ys.shape)) zs = self.zs[self.startY:self.endY, self.startX:self.endX] logger.info("zs sub space array %s,%s " % (zs.shape)) return xs, ys, zs else: return self.xs, self.ys, self.zs def _getIntelligentInitialValues(self): """If possible we can auto set the initial parameters to intelligent guesses user can always overwrite them """ logger.warning("Dummy function should not be called directly") return #in python this should be a pass statement. I.e. user has to overwrite this def fitFunc(self, data, *p): """Function that we are trying to fit to. """ logger.error("Dummy function should not be called directly") return #in python this should be a pass statement. I.e. user has to overwrite this def _setCalculatedValues(self, modelFitResult): """updates calculated values with calculated argument """ parametersResult = modelFitResult.params for variable in self.variablesList: variable.calculatedValue = parametersResult[variable.name].value def _setCalculatedValuesErrors(self, modelFitResult): """given the covariance matrix returned by scipy optimize fit convert this into stdeviation errors for parameters list and updated the stdevError attribute of variables""" parametersResult = modelFitResult.params for variable in self.variablesList: variable.stdevError = parametersResult[variable.name].stderr def _setInitialValues(self, guesses): """updates calculated values with calculated argument """ c = 0 for variable in self.variablesList: variable.initialValue = guesses[c] c += 1 def deriveCalculatedParameters(self): """Wrapper for subclass definition of deriving calculated parameters can put more general calls in here""" if self.fitted: self._deriveCalculatedParameters() def _deriveCalculatedParameters(self): """Should be implemented by subclass. should update all variables in calculate parameters list""" logger.error("Should only be called by subclass") return def _fit_routine(self): """This function performs the fit in an appropriate thread and updates necessary values when the fit has been performed""" self.fitting = True if self.fitThread and self.fitThread.isAlive(): logger.warning( "Fitting is already running. You should wait till this fit has timed out before a new thread is started...." ) #logger.warning("I will start a new fitting thread but your previous thread may finish at some undetermined time. you probably had bad starting conditions :( !") return self.fitThread = FitThread() #new fitting thread self.fitThread.fitReference = self self.fitThread.isCurrentFitThread = True # user can create multiple fit threads on a particular fit but only the latest one will have an effect in the GUI self.fitThread.start() self.fittingStatus = self.currentlyFittingStatus def _perform_fit(self): """Perform the fit using scipy optimise curve fit. We must supply x and y as one argument and zs as anothger. in the form xs: 0 1 2 0 1 2 0 ys: 0 0 0 1 1 1 2 zs: 1 5 6 1 9 8 2 Hence the use of repeat and tile in positions and unravel for zs initially xs,ys is a linspace array and zs is a 2d image array """ if self.xs is None or self.ys is None or self.zs is None: logger.warning( "attempted to fit data but had no data inside the Fit object. set xs,ys,zs first" ) return ([], []) params = self._getParameters() if self.fitSubSpace: #fit only the sub space #create xs, ys and zs which are appropriate slices of the arrays xs, ys, zs = self._get_subSpaceArrays() else: #fit the whole array of data (slower) xs, ys, zs = self.xs, self.ys, self.zs positions = scipy.array([ scipy.tile(xs, len(ys)), scipy.repeat(ys, len(xs)) ]) #for creating data necessary for gauss2D function if self.fitTimeLimitBool: modelFitResult = self.lmfitModel.fit(scipy.ravel(zs), positions=positions, params=params, iter_cb=self.getFitCallback( time.time())) else: #no iter callback modelFitResult = self.lmfitModel.fit(scipy.ravel(zs), positions=positions, params=params) return modelFitResult def getFitCallback(self, startTime): """returns the callback function that is called at every iteration of fit to check if it has been running too long""" def fitCallback(params, iter, resid, *args, **kws): """check the time and compare to start time """ if time.time() - startTime > self.fitTimeLimit: raise FitException("Fit time exceeded user limit") return fitCallback def _performFitButton_fired(self): self._fit_routine() def _getInitialParametersButton_fired(self): self._intelligentInitialValues() def _drawRequestButton_fired(self): """tells the imageInspector to try and draw this fit as an overlay contour plot""" self.imageInspectorReference.addFitPlot(self) def _setSizeButton_fired(self): """use the sigmaX and sigmaY from the current fit to overwrite the inTrapSizeX and inTrapSizeY parameters in the Physics Instance""" self.physics.inTrapSizeX = abs(self.sigmax.calculatedValue) self.physics.inTrapSizeY = abs(self.sigmay.calculatedValue) def _getFitFuncData(self): """if data has been fitted, this returns the zs data for the ideal fitted function using the calculated paramters""" positions = [ scipy.tile(self.xs, len(self.ys)), scipy.repeat(self.ys, len(self.xs)) ] #for creating data necessary for gauss2D function zsravelled = self.fitFunc(positions, *self._getCalculatedValues()) return zsravelled.reshape(self.zs.shape) def _logAnalyserSelectButton_fired(self): """open a fast file editor for selecting many files """ fileDialog = FileDialog(action="open files") fileDialog.open() if fileDialog.return_code == pyface.constant.OK: self.logAnalysers = fileDialog.paths logger.info("selected log analysers: %s " % self.logAnalysers) self.logAnalyserDisplayString = str( [os.path.split(path)[1] for path in self.logAnalysers]) def runSingleAnalyser(self, module): """runs the logAnalyser module calling the run function and returns the columnNames and values as a list""" exec("import logAnalysers.%s as currentAnalyser" % module) reload( currentAnalyser ) #in case it has changed..#could make this only when user requests #now the array also contains the raw image as this may be different to zs if you are using a processor if hasattr(self.imageInspectorReference, "rawImage"): rawImage = self.imageInspectorReference.rawImage else: rawImage = None return currentAnalyser.run([self.xs, self.ys, self.zs, rawImage], self.physics.variables, self.variablesList, self.calculatedParametersList) def runAnalyser(self): """ if logAnalyserBool is true we perform runAnalyser at the end of _log_fit runAnalyser checks that logAnalyser exists and is a python script with a valid run()function it then performs the run method and passes to the run function: -the image data as a numpy array -the xml variables dictionary -the fitted paramaters -the derived values""" for logAnalyser in self.logAnalysers: if not os.path.isfile(logAnalyser): logger.error( "attempted to runAnalyser but could not find the logAnalyser File: %s" % logAnalyser) return #these will contain the final column names and values finalColumns = [] finalValues = [] #iterate over each selected logAnalyser get the column names and values and add them to the master lists for logAnalyser in self.logAnalysers: directory, module = os.path.split(logAnalyser) module, ext = os.path.splitext(module) if ext != ".py": logger.error("file was not a python module. %s" % logAnalyser) else: columns, values = self.runSingleAnalyser(module) finalColumns.extend(columns) finalValues.extend(values) return finalColumns, finalValues def mostRecentModelFitReport(self): """returns the lmfit fit report of the most recent lmfit model results object""" if self.mostRecentModelResult is not None: return lmfit.fit_report(self.mostRecentModelResult) + "\n\n" else: return "No fit performed" def getCalculatedParameters(self): """useful for print returns tuple list of calculated parameter name and value """ return [(_.name, _.value) for _ in self.calculatedParametersList] def _log_fit(self): if self.logName == "": logger.warning("no log file defined. Will not log") return #generate folders if they don't exist if self.logToNas is not True: logFolder = os.path.join(self.logDirectory, self.logName) else: logFolder = os.path.join(self.logDirectoryNas, self.logName) if not os.path.isdir(logFolder): logger.info("creating a new log folder %s" % logFolder) os.mkdir(logFolder) imagesFolder = os.path.join(logFolder, "images") if not os.path.isdir(imagesFolder): logger.info("creating a new images Folder %s" % imagesFolder) os.mkdir(imagesFolder) commentsFile = os.path.join(logFolder, "comments.txt") if not os.path.exists(commentsFile): logger.info("creating a comments file %s" % commentsFile) open(commentsFile, "a+").close() #create a comments file in every folder! firstSequenceCopy = os.path.join(logFolder, "copyOfInitialSequence.ctr") if not os.path.exists(firstSequenceCopy): logger.info("creating a copy of the first sequence %s -> %s" % (self.latestSequence, firstSequenceCopy)) shutil.copy(self.latestSequence, firstSequenceCopy) if self.imageInspectorReference.model.imageMode == "process raw image": #if we are using a processor, save the details of the processor used to the log folder processorParamtersFile = os.path.join(logFolder, "processorOptions.txt") processorPythonScript = os.path.join(logFolder, "usedProcessor.py") #TODO! if not os.path.exists(processorParamtersFile): with open(processorParamtersFile, "a+") as processorParamsFile: string = str(self.imageInspectorReference.model. chosenProcessor) + "\n" string += str(self.imageInspectorReference.model.processor. optionsDict) processorParamsFile.write(string) logger.debug("finished all checks on log folder") #copy current image try: shutil.copy(self.imageInspectorReference.selectedFile, imagesFolder) if self.imageInspectorReference.selectedFile.endswith("_X2.tif"): shutil.copy( self.imageInspectorReference.selectedFile.replace( "_X2.tif", "_X1.tif"), imagesFolder) except IOError as e: logger.error("Could not copy image. Got IOError: %s " % e.message) except Exception as e: logger.error("Could not copy image. Got %s: %s " % (type(e), e.message)) raise e logger.info("copying current image") self.logFile = os.path.join(logFolder, self.logName + ".csv") #analyser logic if self.logAnalyserBool: #run the analyser script as requested logger.info( "log analyser bool enabled... will attempt to run analyser script" ) analyserResult = self.runAnalyser() logger.info("analyser result = %s " % list(analyserResult)) if analyserResult is None: analyserColumnNames = [] analyserValues = [] #analyser failed. continue as if nothing happened else: analyserColumnNames, analyserValues = analyserResult else: #no analyser enabled analyserColumnNames = [] analyserValues = [] if not os.path.exists(self.logFile): variables = [_.name for _ in self.variablesList] calculated = [_.name for _ in self.calculatedParametersList] times = ["datetime", "epoch seconds"] info = ["img file name"] xmlVariables = self.getXmlVariables() columnNames = times + info + variables + calculated + xmlVariables + analyserColumnNames with open( self.logFile, 'ab+' ) as logFile: # note use of binary file so that windows doesn't write too many /r writer = csv.writer(logFile) writer.writerow(columnNames) #column names already exist so... logger.debug("copying current image") variables = [_.calculatedValue for _ in self.variablesList] calculated = [_.value for _ in self.calculatedParametersList] now = time.time() #epoch seconds timeTuple = time.localtime(now) date = time.strftime("%Y-%m-%dT%H:%M:%S", timeTuple) times = [date, now] info = [self.imageInspectorReference.selectedFile] xmlVariables = [ self.physics.variables[varName] for varName in self.getXmlVariables() ] data = times + info + variables + calculated + xmlVariables + analyserValues with open(self.logFile, 'ab+') as logFile: writer = csv.writer(logFile) writer.writerow(data) def _logLastFitButton_fired(self): """logs the fit. User can use this for non automated logging. i.e. log particular fits""" self._log_fit() def _removeLastFitButton_fired(self): """removes the last line in the log file """ logFolder = os.path.join(self.logDirectory, self.logName) self.logFile = os.path.join(logFolder, self.logName + ".csv") if self.logFile == "": logger.warning("no log file defined. Will not log") return if not os.path.exists(self.logFile): logger.error( "cant remove a line from a log file that doesn't exist") with open(self.logFile, 'r') as logFile: lines = logFile.readlines() with open(self.logFile, 'wb') as logFile: logFile.writelines(lines[:-1]) def saveLastFit(self): """saves result of last fit to a txt/csv file. This can be useful for live analysis or for generating sequences based on result of last fit""" try: with open( self.imageInspectorReference.cameraModel + "-" + self.physics.species + "-" + "lastFit.csv", "wb") as lastFitFile: writer = csv.writer(lastFitFile) writer.writerow(["time", time.time()]) for variable in self.variablesList: writer.writerow([variable.name, variable.calculatedValue]) for variable in self.calculatedParametersList: writer.writerow([variable.name, variable.value]) except Exception as e: logger.error("failed to save last fit to text file. message %s " % e.message) def _chooseVariablesButtons_fired(self): self.xmlLogVariables = self.chooseVariables() def _usePreviousFitValuesButton_fired(self): """update the guess initial values with the value from the last fit """ logger.info( "use previous fit values button fired. loading previous initial values" ) self._setInitialValues(self._getCalculatedValues()) def getXmlVariables(self): if self.logAllVariables: return sorted(self.physics.variables.keys()) else: return self.xmlLogVariables def chooseVariables(self): """Opens a dialog asking user to select columns from a data File that has been selected. THese are then returned as a string suitable for Y cols input""" columns = self.physics.variables.keys() columns.sort() values = zip(range(0, len(columns)), columns) checklist_group = traitsui.Group( '10', # insert vertical space traitsui.Label('Select the additional variables you wish to log'), traitsui.UItem('columns', style='custom', editor=traitsui.CheckListEditor(values=values, cols=6)), traitsui.UItem('selectAllButton')) traits_view = traitsui.View(checklist_group, title='CheckListEditor', buttons=['OK'], resizable=True, kind='livemodal') col = ColumnEditor(numberOfColumns=len(columns)) try: col.columns = [ columns.index(varName) for varName in self.xmlLogVariables ] except Exception as e: logger.error( "couldn't selected correct variable names. Returning empty selection" ) logger.error("%s " % e.message) col.columns = [] col.edit_traits(view=traits_view) logger.debug("value of columns selected = %s ", col.columns) logger.debug("value of columns selected = %s ", [columns[i] for i in col.columns]) return [columns[i] for i in col.columns] def _logLibrarianButton_fired(self): """opens log librarian for current folder in logName box. """ logFolder = os.path.join(self.logDirectory, self.logName) if not os.path.isdir(logFolder): logger.error( "cant open librarian on a log that doesn't exist.... Could not find %s" % logFolder) return librarian = plotObjects.logLibrarian.Librarian(logFolder=logFolder) librarian.edit_traits() def _drawSubSpace_changed(self): newVisibility = self.drawSubSpace and self.fitSubSpace self.imageInspectorReference.ROIPolyPlot.visible = newVisibility self._startX_changed() # update ROI data for plot def _fitSubSpace_changed(self): self._drawSubSpace_changed() def _startX_changed(self): if self.imageInspectorReference is None: return # not yet initialized yet self.imageInspectorReference.ROIPolyPlot.index = chaco.ArrayDataSource( [self.startX, self.endX, self.endX, self.startX]) self.imageInspectorReference.ROIPolyPlot.value = chaco.ArrayDataSource( [self.startY, self.startY, self.endY, self.endY]) _endX_changed = _startX_changed _startY_changed = _startX_changed _endY_changed = _startX_changed
class CSVJoiner(tr.HasStrictTraits): open_csv_files = tr.Button csv_files = tr.List(CSVFile) num_of_first_lines_to_show = tr.Range(low=0, high=10**9, value=10, mode='spinner') num_of_last_lines_to_show = tr.Range(low=0, high=10**9, value=10, mode='spinner') selected = tr.Instance(CSVFile) join_csv_files = tr.Button accumulate_time = tr.Bool files_end_with_empty_line = tr.Bool(True) columns_headers = tr.List time_column = tr.Enum(values='columns_headers') progress = tr.Int def _join_csv_files_fired(self): output_file_path = self.get_output_file_path() with open(output_file_path, 'w') as outfile: for csv_file, i in zip(self.csv_files, range(len(self.csv_files))): current_line = 1 num_of_first_lines_to_skip = csv_file.first_lines_to_skip num_of_last_lines_to_skip = csv_file.last_lines_to_skip last_line_to_write = csv_file.get_lines_number( ) - num_of_last_lines_to_skip progress_of_a_file = 1.0 / len(self.csv_files) initial_progress = i / len(self.csv_files) with open(csv_file.path) as opened_csv_file: for line in opened_csv_file: if current_line > num_of_first_lines_to_skip and current_line <= last_line_to_write: outfile.write(line) self.progress = int( (initial_progress + progress_of_a_file * (current_line / last_line_to_write)) * 100) current_line += 1 if not self.files_end_with_empty_line: outfile.write('\n') self.progress = 100 dialog = MessageDialog(title='Finished!', message='Files joined successfully, see "' + output_file_path + '"') dialog.open() def get_output_file_path(self): file_path = self.csv_files[0].path file_path_without_ext = os.path.splitext(file_path)[0] file_ext = os.path.splitext(file_path)[1] return file_path_without_ext + '_joined' + file_ext def _accumulate_time_changed(self): pass # if self.csv_files == []: # return # np.array(pd.read_csv( # self.file_csv, delimiter=self.delimiter, decimal=self.decimal, # nrows=1, header=None # ) # )[0] # if self.accumulate_time: # class TimeColumnChooser(tr.HasTraits): # time_column = tr.Enum(values = 'columns_headers') # chooser = TimeColumnChooser() # chooser.configure_traits(kind='modal') def _num_of_first_lines_to_show_changed(self): for file in self.csv_files: file.num_of_first_lines_to_show = self.num_of_first_lines_to_show def _num_of_last_lines_to_show_changed(self): for file in self.csv_files: file.num_of_last_lines_to_show = self.num_of_last_lines_to_show def _open_csv_files_fired(self): extensions = ['*.csv', '*.txt'] # handle only one extension... wildcard = ';'.join(extensions) dialog = pf.FileDialog(title='Select csv files', action='open files', wildcard=wildcard, default_path=os.path.expanduser("~")) result = dialog.open() csv_files_paths = [] # Test if the user opened a file to avoid throwing an exception # if he doesn't if result == pf.OK: csv_files_paths = dialog.paths else: return self.csv_files = [] for file_path in csv_files_paths: csv_file = CSVFile( path=file_path, num_of_first_lines_to_show=self.num_of_first_lines_to_show, num_of_last_lines_to_show=self.num_of_last_lines_to_show, ) self.csv_files.append(csv_file) # ========================================================================= # Configuration of the view # ========================================================================= traits_view = ui.View( ui.VGroup( ui.UItem('open_csv_files', width=150), ui.HGroup(ui.Item('num_of_first_lines_to_show'), ui.spring), ui.HGroup(ui.Item('num_of_last_lines_to_show'), ui.spring), ui.HGroup( ui.Item('files_end_with_empty_line'), # ui.Item('accumulate_time', enabled_when='False'), ui.spring), ui.VGroup( ui.Item('csv_files', show_label=False, style='custom', editor=ui.ListEditor(use_notebook=True, deletable=False, selected='selected', export='DockWindowShell', page_name='.name'))), ui.HGroup( ui.UItem('join_csv_files', width=150), ui.UItem('progress', editor=ProgressEditor(min=0, max=100))), show_border=True), title='CSV files joiner', resizable=True, width=0.6, height=0.7)
class Matplotlibify(traits.HasTraits): logFilePlotReference = traits.Instance( logFilePlots.plotObjects.logFilePlot.LogFilePlot) plotPropertiesList = traits.List(PlotProperties) logFilePlot1 = traits.Any() logFilePlot2 = traits.Any() logFilePlotsReference = traits.Instance( logFilePlots.LogFilePlots) #refernce to logFilePlots object isPriviliged = traits.Bool(False) hardCodeLegendBool = traits.Bool( False, desc= "click if you want to write your own legend otherwise it will generate legend based on series and legend replacement dict" ) hardCodeLegendString = traits.String( "", desc="comma seperated string for each legend entry") #xLim = traits.Tuple() replacementStrings = {} savedPrintsDirectory = traits.Directory( os.path.join("\\\\ursa", "AQOGroupFolder", "Experiment Humphry", "Data", "savedPrints")) showWaterMark = traits.Bool(True) matplotlibifyMode = traits.Enum("default", "dual plot") generatePlotScriptButton = traits.Button("generate plot") showPlotButton = traits.Button("show") #templatesFolder = os.path.join( os.path.expanduser('~'),"Google Drive","Thesis","python scripts","matplotlibify") templatesFolder = os.path.join("\\\\ursa", "AQOGroupFolder", "Experiment Humphry", "Experiment Control And Software", "LogFilePlots", "matplotlibify", "templates") templateFile = traits.File( os.path.join(templatesFolder, "matplotlibifyDefaultTemplate.py")) generatedScriptLocation = traits.File( os.path.join(os.path.expanduser('~'), "Google Drive", "Thesis", "python scripts", "matplotlibify", "debug.py")) saveToOneNote = traits.Button("Save to OneNote") printButton = traits.Button("print") dualPlotMode = traits.Enum('sharedXY', 'sharedX', 'sharedY', 'stacked', 'stackedX', 'stackedY') logLibrarianReference = None secondPlotGroup = traitsui.VGroup( traitsui.Item("matplotlibifyMode", label="mode"), traitsui.HGroup( traitsui.Item("logFilePlot1", visible_when="matplotlibifyMode=='dual plot'"), traitsui.Item("logFilePlot2", visible_when="matplotlibifyMode=='dual plot'"), traitsui.Item('dualPlotMode', visible_when="matplotlibifyMode=='dual plot'", show_label=False)), ) plotPropertiesGroup = traitsui.Item( "plotPropertiesList", editor=traitsui.ListEditor(style="custom"), show_label=False, resizable=True) generalGroup = traitsui.VGroup( traitsui.Item("showWaterMark", label="show watermark"), traitsui.HGroup( traitsui.Item("hardCodeLegendBool", label="hard code legend?"), traitsui.Item("hardCodeLegendString", show_label=False, visible_when="hardCodeLegendBool")), traitsui.Item("templateFile"), traitsui.Item("generatedScriptLocation", visible_when='isPriviliged'), traitsui.Item('generatePlotScriptButton', visible_when='isPriviliged'), traitsui.Item('showPlotButton'), traitsui.Item( 'saveToOneNote', enabled_when='True' ), # was deactivated for some time, probably there was an error, I try to debug this now traitsui.Item('printButton')) traits_view = traitsui.View(secondPlotGroup, plotPropertiesGroup, generalGroup, resizable=True, kind='live') def __init__(self, **traitsDict): super(Matplotlibify, self).__init__(**traitsDict) self.plotPropertiesList = [PlotProperties(self.logFilePlotReference)] self.generateReplacementStrings() self.add_trait( "logFilePlot1", traits.Trait( self.logFilePlotReference.logFilePlotsTabName, { lfp.logFilePlotsTabName: lfp for lfp in self.logFilePlotsReference.lfps })) self.add_trait( "logFilePlot2", traits.Trait( self.logFilePlotReference.logFilePlotsTabName, { lfp.logFilePlotsTabName: lfp for lfp in self.logFilePlotsReference.lfps })) def generateReplacementStrings(self): self.replacementStrings = {} if self.matplotlibifyMode == 'default': specific = self.plotPropertiesList[ 0].getReplacementStringsSpecific(identifier="") generic = self.getGlobalReplacementStrings() self.replacementStrings.update(specific) self.replacementStrings.update(generic) elif self.matplotlibifyMode == 'dual plot': specific1 = self.plotPropertiesList[ 0].getReplacementStringsSpecific(identifier="lfp1.") specific2 = self.plotPropertiesList[ 1].getReplacementStringsSpecific(identifier="lfp2.") generic = self.getGlobalReplacementStrings() self.replacementStrings.update(specific1) self.replacementStrings.update(specific2) self.replacementStrings.update(generic) for key in self.replacementStrings.keys( ): #wrap strings in double quotes logger.info("%s = %s" % (self.replacementStrings[key], type(self.replacementStrings[key]))) if isinstance(self.replacementStrings[key], (str, unicode)): if self.replacementStrings[key].startswith("def "): continue #if it is a function definition then dont wrap in quotes! else: self.replacementStrings[key] = unicode( self.wrapInQuotes(self.replacementStrings[key])) def getGlobalReplacementStrings(self, identifier=""): """generates the replacement strings that are specific to a log file plot """ return { '{{%shardCodeLegendBool}}' % identifier: self.hardCodeLegendBool, '{{%shardCodeLegendString}}' % identifier: self.hardCodeLegendString, '{{%smatplotlibifyMode}}' % identifier: self.matplotlibifyMode, '{{%sshowWaterMark}}' % identifier: self.showWaterMark, '{{%sdualPlotMode}}' % identifier: self.dualPlotMode } def wrapInQuotes(self, string): return '"%s"' % string def _isPriviliged_default(self): if os.path.exists( os.path.join("C:", "Users", "tharrison", "Google Drive", "Thesis", "python scripts", "matplotlibify")): return True else: return False def _generatedScriptLocation_default(self): root = os.path.join("C:", "Users", "tharrison", "Google Drive", "Thesis", "python scripts", "matplotlibify") head, tail = os.path.split(self.logFilePlotReference.logFile) matplotlibifyName = os.path.splitext(tail)[0] + "-%s-vs-%s" % ( self.plotPropertiesList[0]._yAxisLabel_default(), self.plotPropertiesList[0]._xAxisLabel_default()) baseName = os.path.join(root, matplotlibifyName) filename = baseName + ".py" c = 0 while os.path.exists(filename + ".py"): filename = baseName + "-%s.py" % c c += 1 return filename def replace_all(self, text, replacementDictionary): for placeholder, new in replacementDictionary.iteritems(): text = text.replace(placeholder, str(new)) return text def _generatePlotScriptButton_fired(self): self.writePlotScriptToFile(self.generatedScriptLocation) def writePlotScriptToFile(self, path): """writes the script that generates the plot to the path """ logger.info("attempting to generate matplotlib script...") self.generateReplacementStrings() with open(self.templateFile, "rb") as template: text = self.replace_all(template.read(), self.replacementStrings) with open(self.generatedScriptLocation, "wb") as output: output.write(text) logger.info("succesfully generated matplotlib script at location %s " % self.generatedScriptLocation) def autoSavePlotWithMatplotlib(self, path): """runs the script with an appended plt.save() and plt.close("all")""" logger.info("attempting to save matplotlib plot...") self.generateReplacementStrings() with open(self.templateFile, "rb") as template: text = self.replace_all(template.read(), self.replacementStrings) ns = {} saveCode = "\n\nplt.savefig(r'%s', dpi=300)\nplt.close('all')" % path logger.info("executing save statement:%s" % saveCode) text += saveCode exec text in ns logger.info("exec completed succesfully...") def _showPlotButton_fired(self): logger.info("attempting to show matplotlib plot...") self.generateReplacementStrings() with open(self.templateFile, "rb") as template: text = self.replace_all(template.read(), self.replacementStrings) ns = {} exec text in ns logger.info("exec completed succesfully...") def _saveToOneNote_fired(self): """calls the lfp function to save the file in the log folder and then save it to oneNote. THis way there is no oneNote code in matplotlibify""" if self.logLibrarianReference is None: self.logFilePlotReference.savePlotAsImage(self) else: self.logFilePlotReference.savePlotAsImage( self, self.logLibrarianReference) def _matplotlibifyMode_changed(self): """change default template depending on whether or not this is a double axis plot """ if self.matplotlibifyMode == "default": self.templateFile = os.path.join( self.templatesFolder, "matplotlibifyDefaultTemplate.py") self.plotPropertiesList = [ PlotProperties(self.logFilePlotReference) ] elif self.matplotlibifyMode == "dual plot": self.templateFile = os.path.join( self.templatesFolder, "matplotlibifyDualPlotTemplate.py") if len(self.plotPropertiesList) > 1: self.plotPropertiesList[1] = PlotProperties( self.logFilePlot2_) #or should it be logFilePlot2_??? logger.info("chanigng second element of plot properties list") elif len(self.plotPropertiesList) == 1: self.plotPropertiesList.append( PlotProperties(self.logFilePlot2_)) logger.info("appending to plot properties list") else: logger.error( "there only be 1 or 2 elements in plot properties but found %s elements" % len(self.plotPropertiesList)) def _logFilePlot1_changed(self): """logFilePlot1 changed so update plotPropertiesList """ logger.info("logFilePlot1 changed. updating plotPropertiesList") self.plotPropertiesList[0] = PlotProperties(self.logFilePlot1_) def _logFilePlot2_changed(self): logger.info("logFilePlot2 changed. updating plotPropertiesList") self.plotPropertiesList[1] = PlotProperties(self.logFilePlot2_) def dualPlotModeUpdates(self): """called when either _logFilePlot1 or _logFilePLot2 change """ if (self.logFilePlot1_.xAxis == self.logFilePlot2_.xAxis ): #Twin X 2 y axes mode if self.logFilePlot1_.yAxis == self.logFilePlot2_.yAxis: self.dualPlotMode = 'sharedXY' else: self.dualPlotMode = 'sharedX' elif self.logFilePlot1_.yAxis == self.logFilePlot2_.yAxis: self.dualPlotMode = 'sharedY' else: self.dualPlotMode = 'stacked' def _printButton_fired(self): """uses windows built in print image functionality to send png of plot to printer """ logFolder, tail = os.path.split(self.logFilePlotReference.logFile) #logName = tail.strip(".csv")+" - "+str(self.selectedLFP.xAxis)+" vs "+str(self.selectedLFP.yAxis) imageFileName = os.path.join(logFolder, "temporary_print.png") self.logFilePlotReference.savePlotAsImage(self, name=imageFileName, oneNote=False) logger.info("attempting to use windows native printing dialog") os.startfile(os.path.normpath(imageFileName), "print") logger.info("saving to savedPrints folder") head, tail = os.path.split(self._generatedScriptLocation_default()) tail = tail.replace(".py", ".png") dst = os.path.join(self.savedPrintsDirectory, tail) shutil.copyfile(os.path.normpath(imageFileName), dst) logger.info("saved to savedPrints folder")
class LogFilePlotFitter(traits.HasTraits): """This class allows the user to fit the data in log file plots with standard functions or a custom function""" model = traits.Trait( "Gaussian", { "Linear": Model(fittingFunctions.linear), "Quadratic": Model(fittingFunctions.quadratic), "Gaussian": Model(fittingFunctions.gaussian), "lorentzian": Model(fittingFunctions.lorentzian), "parabola": Model(fittingFunctions.parabola), "exponential": Model(fittingFunctions.exponentialDecay), "sineWave": Model(fittingFunctions.sineWave), "sineWaveDecay1": Model(fittingFunctions.sineWaveDecay1), "sineWaveDecay2": Model(fittingFunctions.sineWaveDecay2), "sincSquared": Model(fittingFunctions.sincSquared), "sineSquared": Model(fittingFunctions.sineSquared), "sineSquaredDecay": Model(fittingFunctions.sineSquaredDecay), "custom": Model(custom) }, desc="model selected for fitting the data" ) # mapped trait. so model --> string and model_ goes to Model object. see http://docs.enthought.com/traits/traits_user_manual/custom.html#mapped-traits parametersList = traits.List( Parameter, desc="list of parameters for fitting in chosen model") customCode = traits.Code( "def custom(x, param1, param2):\n\treturn param1*param2*x", desc="python code for a custom fitting function") customCodeCompileButton = traits.Button( "compile", desc= "defines the above function and assigns it to the custom model for fitting" ) fitButton = traits.Button( "fit", desc="runs fit on selected data set using selected parameters and model" ) usePreviousFitButton = traits.Button( "use previous fit", desc="use the fitted values as the initial guess for the next fit") guessButton = traits.Button( "guess", desc= "guess initial values from data using _guess function in library. If not defined button is disabled" ) saveFitButton = traits.Button( "save fit", desc="writes fit parameters values and tolerances to a file") cycleAndFitButton = traits.Button( "cycle fit", desc= "fits using current initial parameters, saves fit, copies calculated values to initial guess and moves to next dataset in ordered dict" ) dataSets = collections.OrderedDict( ) #dict mapping dataset name (for when we have multiple data sets) --> (xdata,ydata ) tuple (scipy arrays) e.g. {"myData": (array([1,2,3]), array([1,2,3]))} dataSetNames = traits.List(traits.String) selectedDataSet = traits.Enum(values="dataSetNames") modelFitResult = None logFilePlotReference = None modelFitMessage = traits.String("not yet fitted") isFitted = traits.Bool(False) maxFitTime = traits.Float( 10.0, desc="maximum time fitting can last before abort") statisticsButton = traits.Button("stats") statisticsString = traits.String("statistics not calculated") plotPoints = traits.Int(200, label="Number of plot points") predefinedModelGroup = traitsui.VGroup( traitsui.Item("model", show_label=False), traitsui.Item("object.model_.definitionString", style="readonly", show_label=False, visible_when="model!='custom'")) customFunctionGroup = traitsui.VGroup(traitsui.Item("customCode", show_label=False), traitsui.Item( "customCodeCompileButton", show_label=False), visible_when="model=='custom'") modelGroup = traitsui.VGroup(predefinedModelGroup, customFunctionGroup, show_border=True) dataAndFittingGroup = traitsui.VGroup( traitsui.HGroup( traitsui.Item("selectedDataSet", label="dataset"), traitsui.Item("fitButton", show_label=False), traitsui.Item("usePreviousFitButton", show_label=False), traitsui.Item("guessButton", show_label=False, enabled_when="model_.guessFunction is not None")), traitsui.HGroup(traitsui.Item("cycleAndFitButton", show_label=False), traitsui.Item("saveFitButton", show_label=False), traitsui.Item("statisticsButton", show_label=False)), traitsui.Item("plotPoints"), traitsui.Item("statisticsString", style="readonly"), traitsui.Item("modelFitMessage", style="readonly"), show_border=True) variablesGroup = traitsui.VGroup(traitsui.Item( "parametersList", editor=traitsui.ListEditor(style="custom"), show_label=False, resizable=True), show_border=True, label="parameters") traits_view = traitsui.View(traitsui.Group(modelGroup, dataAndFittingGroup, variablesGroup, layout="split"), resizable=True) def __init__(self, **traitsDict): super(LogFilePlotFitter, self).__init__(**traitsDict) self._set_parametersList() def _set_parametersList(self): """sets the parameter list to the correct values given the current model """ self.parametersList = [ Parameter(name=parameterName, parameter=parameterObject) for (parameterName, parameterObject) in self.model_.parameters.iteritems() ] def _model_changed(self): """updates model and hences changes parameters appropriately""" self._set_parametersList() self._guessButton_fired( ) # will only guess if there is a valid guessing function def _customCodeCompileButton_fired(self): """defines function as defined by user """ exec(self.customCode) self.model_.__init__(custom) self._set_parametersList() def setFitData(self, name, xData, yData): """updates the dataSets dictionary """ self.dataSets[name] = (xData, yData) def cleanValidNames(self, uniqueValidNames): """removes any elements from datasets dictionary that do not have a key in uniqueValidNames""" for dataSetName in self.dataSets.keys(): if dataSetName not in uniqueValidNames: del self.dataSets[dataSetName] def setValidNames(self): """sets list of valid choices for datasets """ self.dataSetNames = self.dataSets.keys() def getParameters(self): """ returns the lmfit parameters object for the fit function""" return lmfit.Parameters( {_.name: _.parameter for _ in self.parametersList}) def _setCalculatedValues(self, modelFitResult): """updates calculated values with calculated argument """ parametersResult = modelFitResult.params for variable in self.parametersList: variable.calculatedValue = parametersResult[variable.name].value def _setCalculatedValuesErrors(self, modelFitResult): """given the covariance matrix returned by scipy optimize fit convert this into stdeviation errors for parameters list and updated the stdevError attribute of variables""" parametersResult = modelFitResult.params for variable in self.parametersList: variable.stdevError = parametersResult[variable.name].stderr def fit(self): params = self.getParameters() x, y = self.dataSets[self.selectedDataSet] self.modelFitResult = self.model_.model.fit(y, x=x, params=params) #self.modelFitResult = self.model_.model.fit(y, x=x, params=params,iter_cb=self.getFitCallback(time.time()))#can also pass fit_kws= {"maxfev":1000} self._setCalculatedValues( self.modelFitResult) #update fitting paramters final values self._setCalculatedValuesErrors(self.modelFitResult) self.modelFitMessage = self.modelFitResult.message if not self.modelFitResult.success: logger.error("failed to fit in LogFilePlotFitter") self.isFitted = True if self.logFilePlotReference is not None: self.logFilePlotReference.plotFit() def getFitCallback(self, startTime): """returns the callback function that is called at every iteration of fit to check if it has been running too long""" def fitCallback(params, iter, resid, *args, **kws): """check the time and compare to start time """ if time.time() - startTime > self.maxFitTime: return True return fitCallback def _fitButton_fired(self): self.fit() def _usePreviousFitButton_fired(self): """update the initial guess value with the fitted values of the parameter """ for parameter in self.parametersList: parameter.initialValue = parameter.calculatedValue def _guessButton_fired(self): """calls _guess function and updates initial fit values accordingly """ print "guess button clicked" if self.model_.guessFunction is None: print "attempted to guess initial values but no guess function is defined. returning without changing initial values" logger.error( "attempted to guess initial values but no guess function is defined. returning without changing initial values" ) return logger.info("attempting to guess initial values using %s" % self.model_.guessFunction.__name__) xs, ys = self.dataSets[self.selectedDataSet] guessDictionary = self.model_.guessFunction(xs, ys) logger.debug("guess results = %s" % guessDictionary) print "guess results = %s" % guessDictionary for parameterName, guessValue in guessDictionary.iteritems(): for parameter in self.parametersList: if parameter.name == parameterName: parameter.initialValue = guessValue def _saveFitButton_fired(self): saveFolder, filename = os.path.split(self.logFilePlotReference.logFile) parametersResult = self.modelFitResult.params logFileName = os.path.split(saveFolder)[1] functionName = self.model_.function.__name__ saveFileName = os.path.join( saveFolder, logFileName + "-" + functionName + "-fitSave.csv") #parse selected data set name to get column names #selectedDataSet is like "aaaa=1.31 bbbb=1.21" seriesColumnNames = [ seriesString.split("=")[0] for seriesString in self.selectedDataSet.split(" ") ] if not os.path.exists(saveFileName): #create column names with open(saveFileName, "ab+") as csvFile: writer = csv.writer(csvFile) writer.writerow( seriesColumnNames + [variable.name for variable in self.parametersList] + [ variable.name + "-tolerance" for variable in self.parametersList ]) with open(saveFileName, "ab+") as csvFile: #write save to file writer = csv.writer(csvFile) seriesValues = [ seriesString.split("=")[1] for seriesString in self.selectedDataSet.split(" ") ] #values of the legend keys so you know what fit was associated with writer.writerow(seriesValues + [ parametersResult[variable.name].value for variable in self.parametersList ] + [ parametersResult[variable.name].stderr for variable in self.parametersList ]) def _cycleAndFitButton_fired(self): logger.info("cycle and fit button pressed") self._fitButton_fired() self._saveFitButton_fired() self._usePreviousFitButton_fired() currentDataSetIndex = self.dataSets.keys().index(self.selectedDataSet) self.selectedDataSet = self.dataSets.keys()[currentDataSetIndex + 1] def _statisticsButton_fired(self): from scipy.stats import pearsonr xs, ys = self.dataSets[self.selectedDataSet] mean = scipy.mean(ys) median = scipy.median(ys) std = scipy.std(ys) minimum = scipy.nanmin(ys) maximum = scipy.nanmax(ys) peakToPeak = maximum - minimum pearsonCorrelation = pearsonr(xs, ys) resultString = "mean=%G , median=%G stdev =%G\nmin=%G,max=%G, pk-pk=%G\nPearson Correlation=(%G,%G)\n(stdev/mean)=%G" % ( mean, median, std, minimum, maximum, peakToPeak, pearsonCorrelation[0], pearsonCorrelation[1], std / mean) self.statisticsString = resultString def getFitData(self): dataX = self.dataSets[self.selectedDataSet][0] # resample x data dataX = np.linspace(min(dataX), max(dataX), self.plotPoints) dataY = self.modelFitResult.eval(x=dataX) return dataX, dataY
class LogFilePlots(traits.HasTraits): """just a several tabbed view of several log file plot objects""" lfps = traits.List( plotObjects.logFilePlot.LogFilePlot) #list of possible fits selectedLFP = traits.Instance(plotObjects.logFilePlot.LogFilePlot) #logFilePlotGroup = traitsui.Group(traitsui.Item("logFilePlotObject", editor = traitsui.InstanceEditor(), style="custom", show_label=False),label="Log File Plotter") logFilePlotsGroup = traitsui.Group(traitsui.Item( 'lfps', style="custom", editor=traitsui.ListEditor(use_notebook=True, deletable=True, selected="selectedLFP", export='DockWindowShell', page_name=".logFilePlotsTabName"), label="logFilePlots", show_label=False), springy=True) autoRefreshTimer = traits.Instance(Timer) autoRefreshDialog = traits.Instance( plotObjects.autoRefreshDialog.AutoRefreshDialog) #for saving and loading default_directory = os.path.join('N:', os.sep, 'Data', 'eagleLogs') file_wildcard = traits.String("CSV Master Settings (*.csv)|All files|*") menubar = traitsmenu.MenuBar( traitsmenu.Menu(traitsui.Action(name='Add Log File Plot', action='_add_lfp'), traitsui.Action(name='Clone selected', action='_add_with_current_lfp'), traitsui.Action(name='Print current', action='_print_current'), traitsui.Action(name='Auto refresh', action='_autoRefresh_dialog'), traitsui.Action(name='Matplotlibify', action='_matplotlibify_dialog'), traitsui.Action(name='Save as', action='_save_as_settings'), traitsui.Action(name='Load as', action='_open_settings'), name="Menu"), ) statusBarString = traits.String() traits_view = traitsui.View(logFilePlotsGroup, title="Log File Plots", statusbar="statusBarString", icon=pyface.image_resource.ImageResource( os.path.join('icons', 'eagles.ico')), resizable=True, menubar=menubar) def __init__(self, N, **traitsDict): """N is initial number of logFilePlots """ super(traits.HasTraits, self).__init__(**traitsDict) self.lfps = [ plotObjects.logFilePlot.LogFilePlot() for c in range(0, N) ] self.selectedLFP = self.lfps[0] for lfp, counter in zip(self.lfps, range(0, N)): lfp.logFilePlotsBool = True lfp.logFilePlotsTabName = "log file plot " + str(counter) lfp.logFilePlotsReference = self def _add_lfp(self): """called from menu. adds a new logfileplot to list and hence to gui in case you run out """ new = plotObjects.logFilePlot.LogFilePlot() new.logFilePlotsBool = True new.logFilePlotsReference = self self.lfps.append(new) def _add_with_current_lfp(self): """called from menu. adds a new logfileplot to list and hence to gui in case you run out """ cloneTraits = [ "logFile", "mode", "masterList", "xAxis", "yAxis", "aggregateAxis", "series", "xLogScale", "yLogScale", "interpretAsTimeAxis", "filterYs", "filterMinYs", "filterMaxYs", "filterXs", "filterMinXs", "filterMaxXs", "filterNaN", "logFilePlotsBool" ] new = self.selectedLFP.clone_traits(traits=cloneTraits, copy="deep") new.logFilePlotsReference = self new.__init__() self.lfps.append(new) def _print_current(self): """opens matplotlibify that allows user to save to one note or print the image """ self.selectedLFP._savePlotButton_fired() def _autoRefresh_dialog(self): """when user clicks autorefresh in the menu this function calls the dialog after user hits ok, it makes the choices of how to setup or stop the timer""" logger.info("auto refresh dialog called") if self.autoRefreshDialog is None: self.autoRefreshDialog = plotObjects.autoRefreshDialog.AutoRefreshDialog( ) self.autoRefreshDialog.configure_traits() logger.info("dialog edit traits finished") if self.autoRefreshDialog.autoRefreshBool: if self.autoRefreshTimer is not None: self.autoRefreshTimer.stop() self.selectedLFP.autoRefreshObject = self.autoRefreshDialog #this gives it all the info it needs self.autoRefreshTimer = Timer( self.autoRefreshDialog.minutes * 60.0 * 1000.0, self.selectedLFP.autoRefresh) logger.info("started auto refresh timer to autorefresh") else: self.selectedLFP.autoRefreshObject = None logger.info("stopping auto refresh") if self.autoRefreshTimer is not None: self.autoRefreshTimer.stop() def _matplotlibify_dialog(self): import matplotlibify.matplotlibify dialog = matplotlibify.matplotlibify.Matplotlibify( logFilePlotReference=self.selectedLFP, logFilePlotsReference=self) dialog.configure_traits() def _save_settings(self, settingsFile): logger.debug("_save_settings call saving settings") with open(settingsFile, 'wb') as sfile: writer = csv.writer(sfile) writer.writerow([-1, 'N', len(self.lfps)]) c = 0 lfp_traits = [ 'mode', 'logFilePlotBool', 'fitLogFileBool', 'autoFitWithRefresh', 'softRefresh', 'errorBarMode', 'logFile', 'xAxis', 'yAxis', 'aggregateAxis', 'masterList', 'series', 'xLogScale', 'yLogScale', 'interpretAsTimeAxis', 'filterYs', 'filterMinYs', 'filterMaxYs', 'filterXs', 'filterMinXs', 'filterMaxXs', 'filterNaN', 'filterSpecific', 'filterSpecificString', 'filterVariableSelector', 'logFilePlotsBool', 'logFilePlotsTabName' ] for lfp in self.lfps: for trait in lfp_traits: try: writer.writerow([ c, trait, getattr(lfp, trait), type(getattr(lfp, trait)) ]) except Exception as e: logger.error(e.message) c += 1 def _load_settings(self, settingsFile): load_df = pandas.read_csv( settingsFile, na_filter=False, header=None, names=['lfp_idx', 'variable', 'value', 'type']) global_settings_df = load_df[load_df['lfp_idx'] == -1] N = int( global_settings_df[global_settings_df['variable'] == 'N'].value[0]) self.__init__(N) for idx in range(0, N): df = load_df[load_df['lfp_idx'] == idx] traits = list(df.variable.values) values = list(df.value.values) types = list(df.type.values) traitsDict = { trait: (value, type_val) for (trait, value, type_val) in zip(traits, values, types) } logger.info("loading lfp %G" % idx) logger.info("dictionary %s" % traitsDict) self.lfps[idx].load_settings(traitsDict) def _save_as_settings(self): """Called when user presses save as in Menu bar """ dialog = FileDialog(action="save as", default_directory=self.default_directory, wildcard=self.file_wildcard) dialog.open() if dialog.return_code == OK: self.settingsFile = dialog.path if self.settingsFile[-4:] != ".csv": self.settingsFile += ".csv" self._save_settings(self.settingsFile) def _open_settings(self): logger.debug("_open_settings call . Opening a settings file") dialog = FileDialog(action="open", default_directory=self.default_directory, wildcard=self.file_wildcard) dialog.open() if dialog.return_code == OK: self.settingsFile = dialog.path self._load_settings(self.settingsFile)
class ImagePlotInspector(traits.HasTraits): #Traits view definitions: settingsGroup = traitsui.VGroup( traitsui.VGroup( traitsui.Item("watchFolderBool", label="Watch Folder?"), traitsui.HGroup(traitsui.Item("selectedFile", label="Select a File"), visible_when="not watchFolderBool"), traitsui.HGroup(traitsui.Item("watchFolder", label="Select a Directory"), visible_when="watchFolderBool"), traitsui.HGroup(traitsui.Item("searchString", label="Filename sub-string"), visible_when="watchFolderBool"), label="File Settings", show_border=True), traitsui.VGroup(traitsui.HGroup('autoRangeColor', 'colorMapRangeLow', 'colorMapRangeHigh'), traitsui.HGroup('horizontalAutoRange', 'horizontalLowerLimit', 'horizontalUpperLimit'), traitsui.HGroup('verticalAutoRange', 'verticalLowerLimit', 'verticalUpperLimit'), label="axis limits", show_border=True), traitsui.VGroup(traitsui.HGroup('object.model.scale', 'object.model.offset'), traitsui.HGroup( traitsui.Item('object.model.pixelsX', label="Pixels X"), traitsui.Item('object.model.pixelsY', label="Pixels Y")), traitsui.HGroup( traitsui.Item('object.model.ODCorrectionBool', label="Correct OD?"), traitsui.Item('object.model.ODSaturationValue', label="OD saturation value")), traitsui.HGroup( traitsui.Item('contourLevels', label="Contour Levels"), traitsui.Item('colormap', label="Colour Map")), traitsui.HGroup( traitsui.Item('fixAspectRatioBool', label="Fix Plot Aspect Ratio?")), traitsui.HGroup( traitsui.Item('updatePhysicsBool', label="Update Physics with XML?")), traitsui.HGroup( traitsui.Item("cameraModel", label="Update Camera Settings to:")), label="advanced", show_border=True), label="settings") plotGroup = traitsui.Group( traitsui.Item('container', editor=ComponentEditor(size=(800, 600)), show_label=False)) fitsGroup = traitsui.Group(traitsui.Item('fitList', style="custom", editor=traitsui.ListEditor( use_notebook=True, selected="selectedFit", deletable=False, export='DockWindowShell', page_name=".name"), label="Fits", show_label=False), springy=True) mainPlotGroup = traitsui.HSplit(plotGroup, fitsGroup, label="Image") fftGroup = traitsui.Group(label="Fourier Transform") physicsGroup = traitsui.Group(traitsui.Item( "physics", editor=traitsui.InstanceEditor(), style="custom", show_label=False), label="Physics") logFilePlotGroup = traitsui.Group(traitsui.Item( "logFilePlotObject", editor=traitsui.InstanceEditor(), style="custom", show_label=False), label="Log File Plotter") eagleMenubar = traitsmenu.MenuBar( traitsmenu.Menu( traitsui.Action(name='Save Image Copy As...', action='_save_image_as'), traitsui.Action(name='Save Image Copy', action='_save_image_default'), name="File", )) traits_view = traitsui.View(settingsGroup, mainPlotGroup, physicsGroup, logFilePlotGroup, buttons=traitsmenu.NoButtons, menubar=eagleMenubar, handler=EagleHandler, title="Experiment Eagle", statusbar="selectedFile", icon=pyface.image_resource.ImageResource( os.path.join('icons', 'eagles.ico')), resizable=True) model = CameraImage() physics = physicsProperties.physicsProperties.PhysicsProperties( ) #create a physics properties object logFilePlotObject = logFilePlot.LogFilePlot() fitList = model.fitList selectedFit = traits.Instance(fits.Fit) drawFitRequest = traits.Event drawFitBool = traits.Bool(False) # true when drawing a fit as well selectedFile = traits.File() watchFolderBool = traits.Bool(False) watchFolder = traits.Directory() searchString = traits.String( desc= "sub string that must be contained in file name for it to be shown in Eagle. Can be used to allow different instances of Eagle to detect different saved images." ) oldFiles = set() contourLevels = traits.Int(15) colormap = traits.Enum(colormaps.color_map_name_dict.keys()) autoRangeColor = traits.Bool(True) colorMapRangeLow = traits.Float colorMapRangeHigh = traits.Float horizontalAutoRange = traits.Bool(True) horizontalLowerLimit = traits.Float horizontalUpperLimit = traits.Float verticalAutoRange = traits.Bool(True) verticalLowerLimit = traits.Float verticalUpperLimit = traits.Float fixAspectRatioBool = traits.Bool(False) updatePhysicsBool = traits.Bool(True) cameraModel = traits.Enum("Custom", "ALTA0", "ANDOR0", "ALTA1", "ANDOR1") #--------------------------------------------------------------------------- # Private Traits #--------------------------------------------------------------------------- _image_index = traits.Instance(chaco.GridDataSource) _image_value = traits.Instance(chaco.ImageData) _cmap = traits.Trait(colormaps.jet, traits.Callable) #--------------------------------------------------------------------------- # Public View interface #--------------------------------------------------------------------------- def __init__(self, *args, **kwargs): super(ImagePlotInspector, self).__init__(*args, **kwargs) #self.update(self.model) self.create_plot() for fit in self.fitList: fit.imageInspectorReference = self fit.physics = self.physics self.selectedFit = self.fitList[0] self.logFilePlotObject.physicsReference = self.physics #self._selectedFile_changed() logger.info("initialisation of experiment Eagle complete") def create_plot(self): # Create the mapper, etc self._image_index = chaco.GridDataSource(scipy.array([]), scipy.array([]), sort_order=("ascending", "ascending")) image_index_range = chaco.DataRange2D(self._image_index) self._image_index.on_trait_change(self._metadata_changed, "metadata_changed") self._image_value = chaco.ImageData(data=scipy.array([]), value_depth=1) image_value_range = chaco.DataRange1D(self._image_value) # Create the contour plots #self.polyplot = ContourPolyPlot(index=self._image_index, self.polyplot = chaco.CMapImagePlot( index=self._image_index, value=self._image_value, index_mapper=chaco.GridMapper(range=image_index_range), color_mapper=self._cmap(image_value_range)) # Add a left axis to the plot left = chaco.PlotAxis(orientation='left', title="y", mapper=self.polyplot.index_mapper._ymapper, component=self.polyplot) self.polyplot.overlays.append(left) # Add a bottom axis to the plot bottom = chaco.PlotAxis(orientation='bottom', title="x", mapper=self.polyplot.index_mapper._xmapper, component=self.polyplot) self.polyplot.overlays.append(bottom) # Add some tools to the plot self.polyplot.tools.append( tools.PanTool(self.polyplot, constrain_key="shift", drag_button="middle")) self.polyplot.overlays.append( tools.ZoomTool(component=self.polyplot, tool_mode="box", always_on=False)) self.lineInspectorX = clickableLineInspector.ClickableLineInspector( component=self.polyplot, axis='index_x', inspect_mode="indexed", write_metadata=True, is_listener=False, color="white") self.lineInspectorY = clickableLineInspector.ClickableLineInspector( component=self.polyplot, axis='index_y', inspect_mode="indexed", write_metadata=True, color="white", is_listener=False) self.polyplot.overlays.append(self.lineInspectorX) self.polyplot.overlays.append(self.lineInspectorY) self.boxSelection2D = boxSelection2D.BoxSelection2D( component=self.polyplot) self.polyplot.overlays.append(self.boxSelection2D) # Add these two plots to one container self.centralContainer = chaco.OverlayPlotContainer(padding=0, use_backbuffer=True, unified_draw=True) self.centralContainer.add(self.polyplot) # Create a colorbar cbar_index_mapper = chaco.LinearMapper(range=image_value_range) self.colorbar = chaco.ColorBar( index_mapper=cbar_index_mapper, plot=self.polyplot, padding_top=self.polyplot.padding_top, padding_bottom=self.polyplot.padding_bottom, padding_right=40, resizable='v', width=30) self.plotData = chaco.ArrayPlotData( line_indexHorizontal=scipy.array([]), line_valueHorizontal=scipy.array([]), scatter_indexHorizontal=scipy.array([]), scatter_valueHorizontal=scipy.array([]), scatter_colorHorizontal=scipy.array([]), fitLine_indexHorizontal=scipy.array([]), fitLine_valueHorizontal=scipy.array([])) self.crossPlotHorizontal = chaco.Plot(self.plotData, resizable="h") self.crossPlotHorizontal.height = 100 self.crossPlotHorizontal.padding = 20 self.crossPlotHorizontal.plot( ("line_indexHorizontal", "line_valueHorizontal"), line_style="dot") self.crossPlotHorizontal.plot( ("scatter_indexHorizontal", "scatter_valueHorizontal", "scatter_colorHorizontal"), type="cmap_scatter", name="dot", color_mapper=self._cmap(image_value_range), marker="circle", marker_size=4) self.crossPlotHorizontal.index_range = self.polyplot.index_range.x_range self.plotData.set_data("line_indexVertical", scipy.array([])) self.plotData.set_data("line_valueVertical", scipy.array([])) self.plotData.set_data("scatter_indexVertical", scipy.array([])) self.plotData.set_data("scatter_valueVertical", scipy.array([])) self.plotData.set_data("scatter_colorVertical", scipy.array([])) self.plotData.set_data("fitLine_indexVertical", scipy.array([])) self.plotData.set_data("fitLine_valueVertical", scipy.array([])) self.crossPlotVertical = chaco.Plot(self.plotData, width=140, orientation="v", resizable="v", padding=20, padding_bottom=160) self.crossPlotVertical.plot( ("line_indexVertical", "line_valueVertical"), line_style="dot") self.crossPlotVertical.plot( ("scatter_indexVertical", "scatter_valueVertical", "scatter_colorVertical"), type="cmap_scatter", name="dot", color_mapper=self._cmap(image_value_range), marker="circle", marker_size=4) self.crossPlotVertical.index_range = self.polyplot.index_range.y_range # Create a container and add components self.container = chaco.HPlotContainer(padding=40, fill_padding=True, bgcolor="white", use_backbuffer=False) inner_cont = chaco.VPlotContainer(padding=40, use_backbuffer=True) inner_cont.add(self.crossPlotHorizontal) inner_cont.add(self.centralContainer) self.container.add(self.colorbar) self.container.add(inner_cont) self.container.add(self.crossPlotVertical) def initialiseFitPlot(self): """called if this is the first Fit Plot to be drawn """ xstep = 1.0 ystep = 1.0 self.contourXS = scipy.linspace(xstep / 2., self.model.pixelsX - xstep / 2., self.model.pixelsX - 1) self.contourYS = scipy.linspace(ystep / 2., self.model.pixelsY - ystep / 2., self.model.pixelsY - 1) logger.debug("contour initialise fit debug. xs shape %s" % self.contourXS.shape) logger.debug("contour initialise xs= %s" % self.contourXS) self._fit_value = chaco.ImageData(data=scipy.array([]), value_depth=1) self.lineplot = chaco.ContourLinePlot( index=self._image_index, value=self._fit_value, index_mapper=chaco.GridMapper( range=self.polyplot.index_mapper.range), levels=self.contourLevels) self.centralContainer.add(self.lineplot) self.plotData.set_data("fitLine_indexHorizontal", self.model.xs) self.plotData.set_data("fitLine_indexVertical", self.model.ys) self.crossPlotVertical.plot( ("fitLine_indexVertical", "fitLine_valueVertical"), type="line", name="fitVertical") self.crossPlotHorizontal.plot( ("fitLine_indexHorizontal", "fitLine_valueHorizontal"), type="line", name="fitHorizontal") logger.debug("initialise fit plot %s " % self.crossPlotVertical.plots) def addFitPlot(self, fit): """add a contour plot on top using fitted data and add additional plots to sidebars (TODO) """ logger.debug("adding fit plot with fit %s " % fit) if not fit.fitted: logger.error( "cannot add a fitted plot for unfitted data. Run fit first") return if not self.drawFitBool: logger.info("first fit plot so initialising contour plot") self.initialiseFitPlot() logger.info("attempting to set fit data") self.contourPositions = [ scipy.tile(self.contourXS, len(self.contourYS)), scipy.repeat(self.contourYS, len(self.contourXS)) ] #for creating data necessary for gauss2D function zsravelled = fit.fitFunc(self.contourPositions, *fit._getCalculatedValues()) # logger.debug("zs ravelled shape %s " % zsravelled.shape) self.contourZS = zsravelled.reshape( (len(self.contourYS), len(self.contourXS))) # logger.debug("zs contour shape %s " % self.contourZS.shape) # logger.info("shape contour = %s " % self.contourZS) self._fit_value.data = self.contourZS self.container.invalidate_draw() self.container.request_redraw() self.drawFitBool = True def update(self, model): logger.info("updating plot") # if self.selectedFile=="": # logger.warning("selected file was empty. Will not attempt to update plot.") # return if self.autoRangeColor: self.colorbar.index_mapper.range.low = model.minZ self.colorbar.index_mapper.range.high = model.maxZ self._image_index.set_data(model.xs, model.ys) self._image_value.data = model.zs self.plotData.set_data("line_indexHorizontal", model.xs) self.plotData.set_data("line_indexVertical", model.ys) if self.drawFitBool: self.plotData.set_data("fitLine_indexHorizontal", self.contourXS) self.plotData.set_data("fitLine_indexVertical", self.contourYS) self.updatePlotLimits() self._image_index.metadata_changed = True self.container.invalidate_draw() self.container.request_redraw() #--------------------------------------------------------------------------- # Event handlers #--------------------------------------------------------------------------- def _metadata_changed(self, old, new): """ This function takes out a cross section from the image data, based on the line inspector selections, and updates the line and scatter plots.""" if self.horizontalAutoRange: self.crossPlotHorizontal.value_range.low = self.model.minZ self.crossPlotHorizontal.value_range.high = self.model.maxZ if self.verticalAutoRange: self.crossPlotVertical.value_range.low = self.model.minZ self.crossPlotVertical.value_range.high = self.model.maxZ if self._image_index.metadata.has_key("selections"): selections = self._image_index.metadata["selections"] if not selections: #selections is empty list return #don't need to do update lines as no mouse over screen. This happens at beginning of script x_ndx, y_ndx = selections if y_ndx and x_ndx: self.plotData.set_data("line_valueHorizontal", self._image_value.data[y_ndx, :]) self.plotData.set_data("line_valueVertical", self._image_value.data[:, x_ndx]) xdata, ydata = self._image_index.get_data() xdata, ydata = xdata.get_data(), ydata.get_data() self.plotData.set_data("scatter_indexHorizontal", scipy.array([xdata[x_ndx]])) self.plotData.set_data("scatter_indexVertical", scipy.array([ydata[y_ndx]])) self.plotData.set_data( "scatter_valueHorizontal", scipy.array([self._image_value.data[y_ndx, x_ndx]])) self.plotData.set_data( "scatter_valueVertical", scipy.array([self._image_value.data[y_ndx, x_ndx]])) self.plotData.set_data( "scatter_colorHorizontal", scipy.array([self._image_value.data[y_ndx, x_ndx]])) self.plotData.set_data( "scatter_colorVertical", scipy.array([self._image_value.data[y_ndx, x_ndx]])) if self.drawFitBool: self.plotData.set_data("fitLine_valueHorizontal", self._fit_value.data[y_ndx, :]) self.plotData.set_data("fitLine_valueVertical", self._fit_value.data[:, x_ndx]) else: self.plotData.set_data("scatter_valueHorizontal", scipy.array([])) self.plotData.set_data("scatter_valueVertical", scipy.array([])) self.plotData.set_data("line_valueHorizontal", scipy.array([])) self.plotData.set_data("line_valueVertical", scipy.array([])) self.plotData.set_data("fitLine_valueHorizontal", scipy.array([])) self.plotData.set_data("fitLine_valueVertical", scipy.array([])) def _colormap_changed(self): self._cmap = colormaps.color_map_name_dict[self.colormap] if hasattr(self, "polyplot"): value_range = self.polyplot.color_mapper.range self.polyplot.color_mapper = self._cmap(value_range) value_range = self.crossPlotHorizontal.color_mapper.range self.crossPlotHorizontal.color_mapper = self._cmap(value_range) # FIXME: change when we decide how best to update plots using # the shared colormap in plot object self.crossPlotHorizontal.plots["dot"][0].color_mapper = self._cmap( value_range) self.crossPlotVertical.plots["dot"][0].color_mapper = self._cmap( value_range) self.container.request_redraw() def _colorMapRangeLow_changed(self): self.colorbar.index_mapper.range.low = self.colorMapRangeLow def _colorMapRangeHigh_changed(self): self.colorbar.index_mapper.range.high = self.colorMapRangeHigh def _horizontalLowerLimit_changed(self): self.crossPlotHorizontal.value_range.low = self.horizontalLowerLimit def _horizontalUpperLimit_changed(self): self.crossPlotHorizontal.value_range.high = self.horizontalUpperLimit def _verticalLowerLimit_changed(self): self.crossPlotVertical.value_range.low = self.verticalLowerLimit def _verticalUpperLimit_changed(self): self.crossPlotVertical.value_range.high = self.verticalUpperLimit def _autoRange_changed(self): if self.autoRange: self.colorbar.index_mapper.range.low = self.minz self.colorbar.index_mapper.range.high = self.maxz def _num_levels_changed(self): if self.num_levels > 3: self.polyplot.levels = self.num_levels self.lineplot.levels = self.num_levels def _colorMapRangeLow_default(self): logger.debug("setting color map rangle low default") return self.model.minZ def _colorMapRangeHigh_default(self): return self.model.maxZ def _horizontalLowerLimit_default(self): return self.model.minZ def _horizontalUpperLimit_default(self): return self.model.maxZ def _verticalLowerLimit_default(self): return self.model.minZ def _verticalUpperLimit_default(self): return self.model.maxZ def _selectedFit_changed(self, selected): logger.debug("selected fit was changed") def _fixAspectRatioBool_changed(self): if self.fixAspectRatioBool: #using zoom range works but then when you reset zoom this function isn't called... # rangeObject = self.polyplot.index_mapper.range # xrangeValue = rangeObject.high[0]-rangeObject.low[0] # yrangeValue = rangeObject.high[1]-rangeObject.low[1] # logger.info("xrange = %s, yrange = %s " % (xrangeValue, yrangeValue)) # aspectRatioSquare = (xrangeValue)/(yrangeValue) # self.polyplot.aspect_ratio=aspectRatioSquare self.centralContainer.aspect_ratio = float( self.model.pixelsX) / float(self.model.pixelsY) #self.polyplot.aspect_ratio = self.model.pixelsX/self.model.pixelsY else: self.centralContainer.aspect_ratio = None #self.polyplot.aspect_ratio = None self.container.request_redraw() self.centralContainer.request_redraw() def updatePlotLimits(self): """just updates the values in the GUI """ if self.autoRangeColor: self.colorMapRangeLow = self.model.minZ self.colorMapRangeHigh = self.model.maxZ if self.horizontalAutoRange: self.horizontalLowerLimit = self.model.minZ self.horizontalUpperLimit = self.model.maxZ if self.verticalAutoRange: self.verticalLowerLimit = self.model.minZ self.verticalUpperLimit = self.model.maxZ def _selectedFile_changed(self): self.model.getImageData(self.selectedFile) if self.updatePhysicsBool: self.physics.updatePhysics() for fit in self.fitList: fit.fitted = False fit.fittingStatus = fit.notFittedForCurrentStatus if fit.autoFitBool: #we should automatically start fitting for this Fit fit._fit_routine( ) #starts a thread to perform the fit. auto guess and auto draw will be handled automatically self.update_view() #update log file plot if autorefresh is selected if self.logFilePlotObject.autoRefresh: try: self.logFilePlotObject.refreshPlot() except Exception as e: logger.error("failed to update log plot - %s...." % e.message) def _cameraModel_changed(self): """camera model enum can be used as a helper. It just sets all the relevant editable parameters to the correct values. e.g. pixels size, etc. cameras: "Andor Ixon 3838", "Apogee ALTA" """ logger.info("camera model changed") if self.cameraModel == "ANDOR0": self.model.pixelsX = 512 self.model.pixelsY = 512 self.physics.pixelSize = 16.0 self.physics.magnification = 2.0 self.searchString = "ANDOR0" elif self.cameraModel == "ALTA0": self.model.pixelsX = 768 self.model.pixelsY = 512 self.physics.pixelSize = 9.0 self.physics.magnification = 0.5 self.searchString = "ALTA0" elif self.cameraModel == "ALTA1": self.model.pixelsX = 768 self.model.pixelsY = 512 self.physics.pixelSize = 9.0 self.physics.magnification = 4.25 self.searchString = "ALTA1" elif self.cameraModel == "ANDOR1": self.model.pixelsX = 512 self.model.pixelsY = 512 self.physics.pixelSize = 16.0 self.physics.magnification = 2.0 self.searchString = "ANDOR1" else: logger.error("unrecognised camera model") self.refreshFitReferences() self.model.getImageData(self.selectedFile) def refreshFitReferences(self): """When aspects of the image change so that the fits need to have properties updated, it should be done by this function""" for fit in self.fitList: fit.endX = self.model.pixelsX fit.endY = self.model.pixelsY def _pixelsX_changed(self): """If pixelsX or pixelsY change, we must send the new arrays to the fit functions """ logger.info("pixels X Change detected") self.refreshFitReferences() self.update(self.model) self.model.getImageData(self.selectedFile) def _pixelsY_changed(self): """If pixelsX or pixelsY change, we must send the new arrays to the fit functions """ logger.info("pixels Y Change detected") self.refreshFitReferences() self.update(self.model) self.model.getImageData(self.selectedFile) @traits.on_trait_change('model') def update_view(self): if self.model is not None: self.update(self.model) def _save_image(self, originalFilePath, newFilePath): """given the original file path this saves a new copy to new File path """ shutil.copy2(originalFilePath, newFilePath) def _save_image_as(self): """ opens a save as dialog and allows user to save a copy of current image to a custom location with a custom name""" originalFilePath = str( self.selectedFile ) #so that this can't be affected by auto update after the dialog is open file_wildcard = str("PNG (*.png)|All files|*") default_directory = os.path.join("\\\\ursa", "AQOGroupFolder", "Experiment Humphry", "Data", "savedEagleImages") dialog = FileDialog(action="save as", default_directory=default_directory, wildcard=file_wildcard) dialog.open() if dialog.return_code == OK: self._save_image(originalFilePath, dialog.path) logger.debug("custom image copy made") def _save_image_default(self): head, tail = os.path.split(self.selectedFile) default_file = os.path.join("\\\\ursa", "AQOGroupFolder", "Experiment Humphry", "Data", "savedEagleImages", tail) self._save_image(self.selectedFile, default_file) logger.debug("default image copy made")
class ExperimentSnake(traits.HasTraits): """Main Experiment Snake GUI that sends arbitrary actions based on the experiment runner sequence and actions that have been set up.""" #mainLog = utilities.TextDisplay() mainLog = outputStream.OutputStream() statusString = traits.String("Press Start Snake to begin...") isRunning = traits.Bool(False) # true when the snake is running sequenceStarted = traits.Bool( False) # flashes true for ~1ms when sequence starts queue = traits.Int(0) variables = traits.Dict( key_trait=traits.Str, value_trait=traits.Float ) #dictionary mapping variable names in Exp control to their values in this sequence timingEdges = traits.Dict( key_trait=traits.Str, value_trait=traits.Float ) #dictionary mapping timing Edge names in Exp control to their values in this sequence statusList = [ ] #eventually will contain the information gathered from experiment Runner each time we poll startAction = traitsui.Action(name='start', action='_startSnake', image=pyface.image_resource.ImageResource( os.path.join('icons', 'start.png'))) stopAction = traitsui.Action(name='stop', action='_stopSnakeToolbar', image=pyface.image_resource.ImageResource( os.path.join('icons', 'stop.png'))) reloadHWAsAction = traitsui.Action( name='reload', action='_reloadHWAsToolbar', image=pyface.image_resource.ImageResource( os.path.join('icons', 'reload.png'))) connectionTimer = traits.Instance( Timer ) # polls the experiment runner and starts off callbacks at appropriate times statusStringTimer = traits.Instance( Timer) #updates status bar at regular times (less freque) getCurrentTimer = traits.Instance( Timer ) #waits for get current to return which marks the beginning of a sequence getCurrentThread = traits.Instance(SocketThread) connectionPollFrequency = traits.Float( 1000.0) #milliseconds defines accuracy you will perform callbacks at statusStringFrequency = traits.Float(2000.0) #milliseconds getCurrentFrequency = traits.Float( 1000.0) #milliseconds should be shorter than the sequence timeRunning = traits.Float(0.0) #how long the sequence has been running totalTime = traits.Float(0.0) # total length of sequence runnerHalted = traits.Bool(True) # true if runner is halted haltedCount = 0 progress = traits.Float(0.0) # % of cycle complete #progressBar = ProgressDialog() hardwareActions = traits.List(hardwareAction.hardwareAction.HardwareAction) examineVariablesDictionary = traits.Instance( variableDictionary.ExamineVariablesDictionary) xmlString = "" # STRING that will contain entire XML File menubar = traitsui.MenuBar( traitsui.Menu( traitsui.Action(name='Start Snake', action='_startSnake'), traitsui.Action(name='Stop Snake', action='_stopSnake'), traitsui.Action(name='Reload', action='_reloadHWAs'), traitsui.Menu(traitsui.Action(name='DEBUG', action='_changeLoggingLevelDebug'), traitsui.Action(name='INFO', action='_changeLoggingLevelInfo'), traitsui.Action(name='WARNING', action='_changeLoggingLevelWarning'), traitsui.Action(name='ERROR', action='_changeLoggingLevelError'), name="Log Level"), name='Menu')) toolbar = traitsui.ToolBar(startAction, stopAction, reloadHWAsAction) mainSnakeGroup = traitsui.VGroup( traitsui.Item('statusString', show_label=False, style='readonly'), traitsui.Item('mainLog', show_label=False, springy=True, style='custom', editor=traitsui.InstanceEditor())) hardwareActionsGroup = traitsui.Group(traitsui.Item( 'hardwareActions', show_label=False, style='custom', editor=traitsui.ListEditor(style="custom")), label="Hardware Actions", show_border=True) variableExaminerGroup = traitsui.Group(traitsui.Item( "examineVariablesDictionary", editor=traitsui.InstanceEditor(), style="custom", show_label=False), label="Variable Examiner") sidePanelGroup = traitsui.VSplit(hardwareActionsGroup, variableExaminerGroup) traits_view = traitsui.View(traitsui.HSplit(sidePanelGroup, mainSnakeGroup, show_labels=True), resizable=True, menubar=menubar, toolbar=toolbar, width=0.5, height=0.75, title="Experiment Snake", icon=pyface.image_resource.ImageResource( os.path.join('icons', 'snakeIcon.ico'))) def __init__(self, **traits): """ takes no arguments to construct the snake. Everything is done through GUI. Snake construction makes a ExperimentSnakeConnection object and writes to the main log window""" super(ExperimentSnake, self).__init__(**traits) self.connection = experimentRunnerConnection.Connection( ) #can override default ports and IP self.hardwareActions = [ hardwareAction.sequenceLoggerHWA.SequenceLogger( 0.0, snakeReference=self), hardwareAction.experimentTablesHWA.ExperimentTables( 0.0, snakeReference=self, enabled=False), hardwareAction.dlicEvapHWA.EvaporationRamp(1.0, snakeReference=self), #hardwareAction.dlicRFSweepHWA.DLICRFSweep(1.0, snakeReference = self,enabled=False), hardwareAction.dlicRFSweepLZHWA.DLICRFSweep(1.0, snakeReference=self, enabled=False), hardwareAction.dlicRFSweepLZWithPowerCtrlHWA.DLICRFSweep( 1.0, snakeReference=self, enabled=False), hardwareAction.dlicRFSweepLZWithPowerCtrl13PreparationHWA. DLICRFSweep(1.0, snakeReference=self, enabled=True), hardwareAction.dlicPiPulseHWA.DLICPiPulse(1.0, snakeReference=self, enabled=False), hardwareAction.evapAttenuationHWA.EvapAttenuation( 1.0, snakeReference=self), hardwareAction.greyMollassesOffsetFreqHWA.GreyMollassesOffset( 1.0, snakeReference=self, enabled=False), hardwareAction.evapAttenuation2HWA.EvapAttenuation( "EvapSnakeAttenuationTimeFinal", snakeReference=self, enabled=False), hardwareAction.picomotorPlugHWA.PicomotorPlug(1.0, snakeReference=self, enabled=False), hardwareAction.windFreakOffsetLockHWA.WindFreak( 0.0, snakeReference=self, enabled=False), hardwareAction.windFreakOffsetLockHighFieldImagingHWA.WindFreak( 0.0, snakeReference=self, enabled=True), hardwareAction.windFreakOffsetLock6ImagingHWA.WindFreak( 2.0, snakeReference=self, enabled=False), hardwareAction.windFreak6To1HWA.WindFreak(2.0, snakeReference=self, enabled=False), hardwareAction.windFreakOffsetLockLaser3.WindFreak( 3.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaZSFreq( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaZSAtten( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaZSEOMFreq( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaZSEOMAtten( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaSpecFreq( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelLiImaging( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelLiImagingDetuning( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelLiPushPulseAttenuation( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelLiPushPulseDetuning( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaDarkSpotAOMFreq( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaDarkSpotAOMAtten( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaMOTFreq( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaMOTAtten( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaMOTEOMAtten( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaImagingDP( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelLiMOTRep( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelLiMOTCool( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelLiOpticalPump( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNa2to2OpticalPumpingFreq( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNa2to2OpticalPumpingAtt( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaHighFieldImagingFreq( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaHighFieldImagingAtt( 1.0, snakeReference=self, enabled=False), hardwareAction.digitalMultimeterCurrentMeasureHWA. DigitalMultimeterMeasurement(1.0, snakeReference=self, enabled=True), hardwareAction.MXGPiPulseHWA.PiPulse(1.0, snakeReference=self, enabled=False), hardwareAction.variableExplorerHWA.VariableExplorer( 2.0, snakeReference=self, enabled=False), hardwareAction.jds6600HWA.JDS6600HWA(1.0, snakeReference=self, enabled=False), hardwareAction.watchdogHWA.WatchdogHWA(18.0, snakeReference=self, enabled=True) ] introString = """Welcome to experiment snake.""" self.mainLog.addLine(introString, 1) def initialiseHardwareActions(self): for hdwAct in self.hardwareActions: if hdwAct.enabled: returnString = hdwAct.init() hdwAct.variablesReference = self.variables self.mainLog.addLine(returnString) def closeHardwareActions(self): """ this function is called when the user presses stop key. it should cleanly close or shutdown all hardware. user must appropriately implement the hardware action close function""" for hdwAct in self.hardwareActions: if hdwAct.initialised: returnString = hdwAct.close() self.mainLog.addLine(returnString) def _startSnake(self): """action call back from menu or toolbar. Simply starts the timer that polls the runner and makes the isRunning bool true """ self.mainLog.addLine("Experiment Snake Started", 1) self.isRunning = True self.getCurrentBlocking() self.initialiseHardwareActions() self.startTimers() def newSequenceStarted(self): """called by GetCurrent Thread at the beginning of every sequence """ if self.isRunning: #otherwise we have already stopped before new sequence began again self.getStatusUpdate() self.mainLog.addLine("New cycle started: %s" % self.statusList[0], 1) self.refreshExamineVariablesDictionary( ) # update the examine variables dictionary to reflect the latest values self.refreshVariableDependentCallbackTimes( ) # if a callback time is a timing edge name or variable name we must pull the value here else: self.mainLog.addLine("final connection closed") for hdwAct in self.hardwareActions: hdwAct.awaitingCallback = True def _stopSnakeToolbar(self): """if snake is stopped, addLine to main log and then run stopSnake """ self.mainLog.addLine( "Experiment Snake Stopped (you should still wait till the end of this sequence before continuing)", 1) self._stopSnake() def _reloadHWAsToolbar(self): """if snake is stopped, addLine to main log and then run stopSnake """ self.mainLog.addLine( "Experiment Snake Stopped (you should still wait till the end of this sequence before continuing)", 1) self._reloadHWAs() def _reloadHWAs(self): """if snake is stopped, addLine to main log and then run stopSnake """ self.mainLog.addLine("Reloading hardware actions (advanced feature)", 3) reload(hardwareAction.hardwareAction) reload(hardwareAction.sequenceLoggerHWA) reload(hardwareAction.dlicEvapHWA) reload(hardwareAction.dlicRFSweepHWA) reload(hardwareAction.dlicRFSweepHWA) reload(hardwareAction.evapAttenuationHWA) reload(hardwareAction.evapAttenuation2HWA) reload(hardwareAction.picomotorPlugHWA) reload(hardwareAction.windFreakOffsetLockHWA) #reload( hardwareAction.AOMChannelHWAs)#CAUSES REFERENCING PROBLEMS! reload(hardwareAction.experimentTablesHWA) reload(hardwareAction.windFreakOffsetLockHighFieldImagingHWA) reload(hardwareAction.greyMollassesOffsetFreqHWA) reload(hardwareAction.dlicRFSweepLZHWA) reload(hardwareAction.digitalMultimeterCurrentMeasureHWA) self.__init__() def stopTimers(self): """stops all timers with error catching """ try: #stop any previous timer, should only have 1 timer at a time if self.connectionTimer is not None: self.connectionTimer.stop() except Exception as e: logger.error( "couldn't stop current timer before starting new one: %s" % e.message) try: #stop any previous timer, should only have 1 timer at a time if self.statusStringTimer is not None: self.statusStringTimer.stop() except Exception as e: logger.error( "couldn't stop current timer before starting new one: %s" % e.message) try: #stop any previous timer, should only have 1 timer at a time if self.getCurrentTimer is not None: self.getCurrentTimer.stop() except Exception as e: logger.error( "couldn't stop current timer before starting new one: %s" % e.message) def _stopSnake(self): """Simply stops the timers, shuts down hardware and sets isRunning bool false """ self.stopTimers() self.closeHardwareActions() self.isRunning = False def startTimers(self): """This timer object polls the experiment runner regularly polling at any time""" #stop any previous timers self.stopTimers() #start timer self.connectionTimer = Timer(self.connectionPollFrequency, self.getStatus) time.sleep(0.1) self.statusStringTimer = Timer(self.statusStringFrequency, self.updateStatusString) time.sleep(0.1) self.getCurrentTimer = Timer(self.getCurrentFrequency, self.getCurrent) """Menu action function to change logger level """ logger.info("timers started") def getStatus(self): """calls the connection objects get status function and updates the statusList """ logger.debug("starting getStatus") try: self.getStatusUpdate() self.checkForCallback() except Exception as e: logger.error("error in getStatus Function") logger.error("error: %s " % e.message) self.mainLog.addLine( "error in getStatus Function. Error: %s" % e.message, 4) def getStatusUpdate(self): """Calls get status and updates times """ try: statusString = self.connection.getStatus() except socket.error as e: logger.error( "failed to get status . message=%s . errno=%s . errstring=%s " % (e.message, e.errno, e.strerror)) self.mainLog.addLine( "Failed to get status from Experiment Runner. message=%s . errno=%s . errstring=%s" % (e.message, e.errno, e.strerror), 3) self.mainLog.addLine( "Cannot update timeRunning - callbacks could be wrong this sequence!", 4) return self.statusList = statusString.split("\n") timeFormat = '%d/%m/%Y %H:%M:%S' timeBegin = datetime.datetime.strptime(self.statusList[2], timeFormat) timeCurrent = datetime.datetime.strptime(self.statusList[3], timeFormat) self.timeRunning = (timeCurrent - timeBegin).total_seconds() logger.debug("time Running = %s " % self.timeRunning) def checkForCallback(self): """if we've received a sequence, we check through all callback times and send off a callback on a hardware action if appropriate""" try: for hdwAct in [ hdwA for hdwA in self.hardwareActions if hdwA.enabled ]: #only iterate through enable hardware actions if hdwAct.awaitingCallback and self.timeRunning >= hdwAct.callbackTime: #callback should be started! try: logger.debug("attempting to callback %s " % hdwAct.hardwareActionName) hdwAct.setVariablesDictionary(self.variables) logger.debug("vars dictionary set to %s " % self.variables) callbackReturnString = hdwAct.callback() self.mainLog.addLine( "%s @ %s secs : %s" % (hdwAct.hardwareActionName, self.timeRunning, callbackReturnString), 2) hdwAct.awaitingCallback = False hdwAct.callbackCounter += 1 except Exception as e: logger.error( "error while performing callback on %s. see error message below" % (hdwAct.hardwareActionName)) logger.error("error: %s " % e.message) self.mainLog.addLine( "error while performing callback on %s. Error: %s" % (hdwAct.hardwareActionName, e.message), 4) except Exception as e: logger.error("error in checkForCallbackFunction") logger.error("error: %s " % e.message) self.mainLog.addLine( "error in checkForCallbackFunction. Error: %s" % e.message, 4) def getCurrent(self): """calls the connection objects get status function and updates the variables dictionary """ if self.getCurrentThread and self.getCurrentThread.isAlive(): #logger.debug( "getCurrent - already waiting - will not start new thread") #removed the above as it fills the log without any useful information self.sequenceStarted = False return else: logger.info("starting getCurrent Thread") self.getCurrentThread = SocketThread() self.getCurrentThread.snakeReference = self # for calling functions of the snake self.getCurrentThread.start() def getCurrentBlocking(self): """calls getCurrent and won't return until XML parsed. unlike above threaded function This is useful when starting up the snake so that we don't start looking for hardware events until a sequence has started and we have received XML""" self.mainLog.addLine("Waiting for next sequence to start") self.xmlString = self.connection.getCurrent( ) # only returns at the beginning of a sequence! Experiment runner then returns the entire XML file logger.debug("length of xml string = %s " % len(self.xmlString)) logger.debug("end of xml file is like [-30:]= %s" % self.xmlString[-30:]) try: root = ET.fromstring(self.xmlString) variables = root.find("variables") self.variables = { child[0].text: float(child[1].text) for child in variables } #timing edges dictionary : name--> value self.timingEdges = { timingEdge.find("name").text: float(timingEdge.find("value").text) for timingEdge in root.find("timing") } self.newSequenceStarted() except ET.ParseError as e: self.mainLog.addLine("Error. Could not parse XML: %s" % e.message, 3) self.mainLog.addLine( "Possible cause is that buffer is full. is XML length %s>= limit %s ????" % (len(self.xmlString), self.connection.BUFFER_SIZE_XML), 3) logger.error("could not parse XML: %s " % self.xmlString) logger.error(e.message) def updateStatusString(self): """update the status string with first element of return of GETSTATUS. similiar to experiment control and camera control. It also does the analysis of progress that doesn't need to be as accurate (e.g. progress bar)""" logger.info("starting update status string") self.statusString = self.statusList[ 0] + "- Time Running = %s " % self.timeRunning self.queue = int(self.statusList[1]) timeFormat = '%d/%m/%Y %H:%M:%S' timeBegin = datetime.datetime.strptime(self.statusList[2], timeFormat) timeEnd = datetime.datetime.strptime(self.statusList[4], timeFormat) self.timeTotal = (timeEnd - timeBegin).total_seconds() if self.timeRunning > self.timeTotal: self.haltedCount += 1 self.runnerHalted = True if self.haltedCount == 0: logger.critical("runner was stopped.") self.mainLog.addLine("Runner stopped!", 3) self.closeHardwareActions() else: if self.haltedCount > 0: self.initialiseHardwareActions() self.haltedCount = 0 self.runnerHalted = False self.progress = 100.0 * self.timeRunning / self.timeTotal def _examineVariablesDictionary_default(self): if len(self.hardwareActions) > 0: logger.debug( "returning first hardware action %s for examineVariablesDictionary default" % self.hardwareActions[0].hardwareActionName) return variableDictionary.ExamineVariablesDictionary( hdwA=self.hardwareActions[0] ) #default is the first in the list else: logger.warning( "hardwareActions list was empty. how should I populate variable examiner...?!." ) return None def updateExamineVariablesDictionary(self, hdwA): """Populates the examineVariablesDictionary Pane appropriately. It is passed the hdwA so that it can find the necessary variables""" self.examineVariablesDictionary.hdwA = hdwA self.examineVariablesDictionary.hardwareActionName = hdwA.hardwareActionName self.examineVariablesDictionary.updateDisplayList() logger.critical("examineVariablesDictionary changed") def refreshExamineVariablesDictionary(self): """calls the updateDisplayList function of examineVariables Dictionary this updates the values in the display list to the latest values in variables dictionary. useful for refereshing at the beginning of a sequence""" self.examineVariablesDictionary.updateDisplayList() logger.info("refreshed examine variables dictionary") def refreshVariableDependentCallbackTimes(self): """if a HWA is variable dependent call back time, we refresh the value using this function. THis should be called in each sequence""" [ hdwA.parseCallbackTime() for hdwA in self.hardwareActions if hdwA.callbackTimeVariableDependent ] def _changeLoggingLevelDebug(self): """Menu action function to change logger level """ logger.setLevel(logging.DEBUG) def _changeLoggingLevelInfo(self): """Menu action function to change logger level """ logger.setLevel(logging.INFO) def _changeLoggingLevelWarning(self): """Menu action function to change logger level """ logger.setLevel(logging.WARNING) def _changeLoggingLevelError(self): """Menu action function to change logger level """ logger.setLevel(logging.ERROR)