diff --git a/EventDetection.py b/EventDetection.py index 7f78379..cdf6af3 100644 --- a/EventDetection.py +++ b/EventDetection.py @@ -1,440 +1,483 @@ import NanoporeClasses as NC import shelve import os import glob from time import sleep import Functions from pprint import pprint import matplotlib.pyplot as plt from matplotlib.ticker import EngFormatter import numpy as np import sys import argparse import datetime from tkinter.filedialog import askopenfilenames,askdirectory import timeit import LoadData timeInSec = EngFormatter(unit='s', places=2) Amp = EngFormatter(unit='A', places=2) #Default parameters extension = '*.log' coefficients = {'a': 0.999, 'E': 0, 'S': 5, 'maxEventLength': 200e-3, # maximal event length to be considered an event 'minEventLength': 600e-6, # maximal event length to be considered an impulse 'fitLength': 3e-3, # minimal event length to be fitted for impulses 'dt': 25, # go back dt points for fitting of impulses 'hbook': 1, 'delta': 0.2e-9, 'deltaRel': None, # If set overrides delta, calculates delta relative to baseline 'ChimeraLowPass': 10e3} def GetParameters(): print("Usage:") print("run(inputData, newExtension=None, newCoefficients={}, outputFile=None, " "force=False, cut=False, verboseLevel=0)") print() print("Default extension:") print(extension) print() print("Default coefficients:") pprint(coefficients) +def eventdetectionwithfilegeneration(file, coefficients, verboseLevel=1, forceRun=False, CutTraces=False): + # Create new class that contains all events + AllEvents = NC.AllEvents() + # Loop over all files in folder + # Extract filename and generate filename to save located events + if verboseLevel >= 1: + print('analysing ' + file) + directory = os.path.dirname(file) + os.sep + 'analysisfiles' + if not os.path.exists(directory): + os.makedirs(directory, exist_ok=True) + filename, file_extension = os.path.splitext(os.path.basename(file)) + savefilename = directory + os.sep + filename + 'data' + shelfFile = shelve.open(savefilename) + if ~forceRun: + try: #Check if file can be loaded + coefficientsloaded=shelfFile['coefficients'] + tEL = shelfFile['TranslocationEvents'] + # If coefficients before are not identical, analysis needs to run again + if (coefficientsloaded == coefficients): + if verboseLevel >= 1: + print('loaded from file') + else: + forceRun = True + except: + # Except if cannot be loaded, analysis needs to run + forceRun = True + pass + + if forceRun: + # Extract list of events for this file + tEL = eventdetection(file, coefficients, verboseLevel, CutTraces) + if verboseLevel >= 1: + print('Saved {} events'.format(len(tEL.events))) + + # Open savefile and save events for this file + shelfFile['TranslocationEvents'] = tEL + shelfFile['coefficients'] = coefficients + if verboseLevel >= 1: + print('saved to file') + shelfFile.close() + # Add events to the initially created class that contains all events + AllEvents.AddEvent(tEL) + return AllEvents def batcheventdetection(folder, extension, coefficients, verboseLevel=1, forceRun=False, CutTraces=False): # Create new class that contains all events AllEvents = NC.AllEvents() print('found ' + str(len(glob.glob(os.path.join(folder, extension)))) + ' files.') # Loop over all files in folder for fullfilename in glob.glob(os.path.join(folder, extension)): # Extract filename and generate filename to save located events if verboseLevel >= 1: print('analysing '+fullfilename) filename, file_extension = os.path.splitext(os.path.basename(fullfilename)) directory = os.path.dirname(fullfilename) + os.sep + 'analysisfiles' if not os.path.exists(directory): os.makedirs(directory,exist_ok=True) savefilename = directory + os.sep + filename + 'data' shelfFile = shelve.open(savefilename) if ~forceRun: try: #Check if file can be loaded coefficientsloaded=shelfFile['coefficients'] tEL = shelfFile['TranslocationEvents'] # If coefficients before are not identical, analysis needs to run again if (coefficientsloaded == coefficients): if verboseLevel >= 1: print('loaded from file') else: forceRun = True except: # Except if cannot be loaded, analysis needs to run forceRun = True pass if forceRun: # Extract list of events for this file tEL = eventdetection(fullfilename, coefficients, verboseLevel, CutTraces) if verboseLevel >= 1: print('Saved {} events'.format(len(tEL.events))) # Open savefile and save events for this file shelfFile['TranslocationEvents'] = tEL shelfFile['coefficients'] = coefficients if verboseLevel >= 1: print('saved to file') shelfFile.close() # Add events to the initially created class that contains all events AllEvents.AddEvent(tEL) return AllEvents def eventdetection(fullfilename, coefficients, verboseLevel=1, CutTraces=False, showFigures=False): """ Function used to find the events of TranslocationEvents class in the raw data in file 'filename'. It calls the function RecursiveLowPassFast to approximatively locate rough events in the data. If a short TranslocationEvent object is detected its type attribute will be changed to 'Impulse' and the meanTrace attribute will take the value of the minimal current value within the event. Then the CUSUM function will be called to build the CUSUM-fit and assign values to the different attributes of the TranslocationEvent objects. Depending on how the CUSUM was able to fit the trace inside and around the event, the type attribute of the TransocationEvent will be set to 'Real' (if the CUSUM fit went well) or 'Rough' (if the CUSUM was not able to fit the trace). Parameters ---------- fullfilename : str Full path to data file. coefficients : dict Contains the parameters for the analysis. verboseLevel : bool, optional 1 by default. It will print strings indicating the progress of the function in the console with different levels of depth. CutTraces : bool, optional False by default. If True, will cut the signal traces around the events to avoid having appended chunks detected as events. showFigures : bool , optional False by default. If True, it will display a simple figure with the shape of the signal. Returns ------- list of TranslocationEvent All the events in the signal. """ if 'ChimeraLowPass' in coefficients: ChimeraLowPass = coefficients['ChimeraLowPass'] else: ChimeraLowPass = None loadedData = LoadData.OpenFile(fullfilename, ChimeraLowPass, True, CutTraces) maxTime = coefficients['maxEventLength'] minTime = coefficients['minEventLength'] fitTime = coefficients['fitLength'] IncludedBaseline = int(1e-2 * loadedData['samplerate']) delta = coefficients['delta'] hbook = coefficients['hbook'] dt = coefficients['dt'] deltaRel = coefficients['deltaRel'] samplerate = loadedData['samplerate'] if verboseLevel >= 1: print('\n Recursive lowpass...', end='') # Call RecursiveLowPassFast to detect events in current trace start_time = timeit.default_timer() if 'i1Cut' in loadedData: events = [] for cutTrace in loadedData['i1Cut']: events.extend(Functions.RecursiveLowPassFast(cutTrace, coefficients, loadedData['samplerate'])) else: events = Functions.RecursiveLowPassFast(loadedData['i1'], coefficients, loadedData['samplerate']) if verboseLevel >= 1: print('done. Calculation took {}'.format(timeInSec.format_data(timeit.default_timer() - start_time))) print('Roughly detected events: {}'.format(len(events))) # Make a new class translocationEventList that contains all the found events in this file translocationEventList = NC.AllEvents() # Plot current file if showFigures: plt.figure(1) plt.cla() timeVals=np.linspace(0, len(loadedData['i1'])/loadedData['samplerate'], num=len(loadedData['i1'])) plt.plot(timeVals,loadedData['i1']) plt.draw() plt.pause(0.001) if verboseLevel >= 1: print('Fine tuning...', end='') # Call RecursiveLowPassFast to detect events in current trace start_time = timeit.default_timer() cusumEvents = 0 # Loop over all roughly detected events for event in events: beginEvent = event[0] endEvent = event[1] localBaseline = event[2] stdEvent = event[3] if 'blockLength' in loadedData: voltI = int(beginEvent / loadedData['blockLength']) else: voltI = int(0) # Check if the start of the event is later than the first few ms in IncludedBaseline if beginEvent > IncludedBaseline: # Extract trace and extract baseline traceforfitting = loadedData['i1'][int(beginEvent - dt):int(endEvent + dt)] # If event is shorter than fitTime if 3 < (endEvent-beginEvent) <= (fitTime * samplerate): # Try to fit and estimate real output = Functions.ImpulseFitting(traceforfitting, localBaseline, samplerate, verbose=(verboseLevel >= 3)) elif (endEvent-beginEvent) < (maxTime * samplerate): output = 2 else: if verboseLevel >= 2: print('Too long event {t:4.0f} ms\n'.format(t=1000*(endEvent-beginEvent)/samplerate)) output = -1 # Don't include in rest of analysis # if output -1 fit failed, if output -2 differential failed if not isinstance(output, int) and len(output) > 1: (ps1, pe1, pe2, rsquared_event, Idrop) = output if verboseLevel >= 3: print('ps1: {}, pe1: {}, pe2: {}, rsquared: {}'.format(ps1, pe1, pe2, rsquared_event)) # length small enough and r squared big enough if (pe2-ps1)/samplerate < minTime and rsquared_event > 0.7: newEvent = NC.TranslocationEvent(fullfilename, 'Impulse') trace = traceforfitting[ps1:pe2] ps1c = beginEvent - 25 + ps1 pe2c = beginEvent - 25 + pe2 traceBefore = loadedData['i1'][int(ps1c) - IncludedBaseline:int(ps1c)] traceAfter = loadedData['i1'][int(pe2c):int(pe2c) + IncludedBaseline] newEvent.SetEvent(trace, ps1c, localBaseline, samplerate, currentDrop=Idrop) newEvent.SetCoefficients(coefficients, loadedData['v1'][voltI]) newEvent.SetBaselineTrace(traceBefore, traceAfter) newEvent.eventLength = (pe1-ps1)/samplerate if verboseLevel >= 2: print('Fitted impulse of {t:1.3f} ms and {i:2.2f} nA and {r:1.2f} R-squared\n'.format( t=newEvent.eventLength*1e3, i=newEvent.currentDrop*1e9, r=rsquared_event)) elif (pe2-ps1)/samplerate < 3 * minTime and rsquared_event < 0.5: if verboseLevel >= 2: print('Bad fit {r:1.2f} R-squared, event ignored.\n'.format(r=rsquared_event)) output = -1 # Don't include in rest of analysis else: if verboseLevel >= 3: print('Too long event for impulse') output = 2 # Good enough for CUSUM fitting if isinstance(output, int) and abs(output) == 2: # CUSUM fit sigma = np.sqrt(stdEvent) # If deltaRel is set calculate delta based on relative value to baseline if deltaRel is not None: delta = deltaRel * localBaseline if verboseLevel >= 2: print('Using relative delta for CUSUM fitting: {}'.format(Amp.format_data(delta))) h = hbook * delta / sigma (mc, kd, krmv) = Functions.CUSUM(traceforfitting, delta, h, verbose=(verboseLevel >= 3)) krmv = [krmvVal + int(beginEvent) - dt + 1 for krmvVal in krmv] if len(krmv) > 1: trace = loadedData['i1'][int(krmv[0]):int(krmv[-1])] traceBefore = loadedData['i1'][int(krmv[0]) - IncludedBaseline:int(krmv[0])] traceAfter = loadedData['i1'][int(krmv[-1]):int(krmv[-1]) + IncludedBaseline] beginEvent = krmv[0] # Adding CUSUM fitted event newEvent = NC.TranslocationEvent(fullfilename, 'CUSUM') newEvent.SetEvent(trace, beginEvent, localBaseline, samplerate) newEvent.SetBaselineTrace(traceBefore, traceAfter) newEvent.SetCoefficients(coefficients, loadedData['v1'][voltI]) newEvent.SetCUSUMVariables(mc, kd, krmv) cusumEvents += 1 if verboseLevel >= 2: print('Fitted CUSUM of {t:1.3f} ms and {i:2.2f} nA\n'.format( t=newEvent.eventLengthCUSUM * 1e3, i=newEvent.currentDropCUSUM * 1e9)) else: trace = loadedData['i1'][int(beginEvent):int(endEvent)] traceBefore = loadedData['i1'][int(beginEvent) - IncludedBaseline:int(beginEvent)] traceAfter = loadedData['i1'][int(endEvent):int(endEvent) + IncludedBaseline] # Adding roughly detected event newEvent = NC.TranslocationEvent(fullfilename, 'Rough') newEvent.SetEvent(trace, beginEvent, localBaseline, samplerate) newEvent.SetBaselineTrace(traceBefore, traceAfter) newEvent.SetCoefficients(coefficients, loadedData['v1'][voltI]) if verboseLevel >= 2: print('CUSUM failed. Adding only roughly located event of {t:1.3f} ms and {i:2.2f} nA\n'.format( t=len(trace) * 1e3/samplerate, i=(np.mean(np.append(traceBefore,traceAfter)) - np.mean(trace) )* 1e9)) if output != -1: # Add event to TranslocationList translocationEventList.AddEvent(newEvent) if verboseLevel >= 1: print('done. Total fitting took {}'.format(timeInSec.format_data(timeit.default_timer() - start_time))) print('{} events fitted with CUSUM\n'.format(cusumEvents)) #Plot events if True if showFigures: minVal = np.max(loadedData['i1'])#-1.05e-9 #np.min(loadedData['i1']) maxVal = np.max(loadedData['i1'])+0.05e-9#-1.05e-9 #np.min(loadedData['i1']) for i in range(len(events)): beginEvent=events[i][0] endEvent = events[i][1] if beginEvent>100 and (endEvent - beginEvent) >= (minTime * loadedData['samplerate']) and (endEvent - beginEvent) < ( coefficients['maxEventLength'] * loadedData['samplerate']): plt.plot([beginEvent/loadedData['samplerate'], beginEvent/loadedData['samplerate']], [minVal, maxVal], 'y-', lw=1) plt.show() return translocationEventList def LoadEvents(loadname): # Check if loadname is a directory or not if os.path.isdir(loadname): # create standard filename for loading loadFile = os.path.join(loadname,os.path.basename(loadname)+'_Events') else: loadFile, file_extension = os.path.splitext(loadname) # Open file and extract TranslocationEvents shelfFile = shelve.open(loadFile) TranslocationEvents = shelfFile['TranslocationEvents'] shelfFile.close() AllEvents = NC.AllEvents() AllEvents.AddEvent(TranslocationEvents) AllEvents.SetFolder(loadname) return AllEvents def run(inputData, newExtension=None, newCoefficients={}, outputFile=None, force=False, cut=False, verboseLevel=0): """ Function used to call all the other functions in the module needed to find the events in raw nanopore experiment data. Parameters ----------- inputData : str Full path to data file. newExtension : str, optional None by default. NewExtension for input directory. newCoefficients : dict Contains the default parameters for the analysis. outputFile : str, optional None by default. Full path to output file. force : bool, optional False by default. cut : bool, optional False by default. False by default. If True, will cut the signal traces around the events to avoid having appended chunks detected as events. verboseLevel : int, optional 0 by default. 0 by default. If higher, it will print various outputs during running Returns ------- AllEvents object All the events. """ if newExtension is None: newExtension = extension for key in newCoefficients: coefficients[key]=newCoefficients[key] if os.path.isdir(inputData): TranslocationEvents = batcheventdetection(inputData, newExtension, coefficients, verboseLevel, force, cut) else: TranslocationEvents = eventdetection(inputData,coefficients, verboseLevel, cut) #Check if list is empty if outputFile is not None and TranslocationEvents.events: if os.path.isdir(inputData): outputData = inputData + os.sep + 'Data' + os.sep + 'Data' + datetime.date.today().strftime("%Y%m%d") LoadData.SaveVariables(outputFile, TranslocationEvents=TranslocationEvents) return True else: return TranslocationEvents if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-i', '--input', help='Input directory or file') parser.add_argument('-e', '--ext', help='Extension for input directory') parser.add_argument('-o', '--output', help='Output file for saving') parser.add_argument('-c', '--coeff', help='Coefficients for selecting events [-C filter E standarddev maxlength minlength', nargs='+') parser.add_argument('-u', '--cut', help='Cut Traces before detecting event, prevent detecting appended chunks as event', action='store_true') parser.add_argument('-f', '--force', help='Force analysis to run (don''t load from file', action='store_true') args = parser.parse_args() inputData = args.input if inputData is None: inputData = askdirectory() outputData = args.output if outputData is None: if os.path.isdir(inputData): outputData = inputData + os.sep + 'Data' + os.sep + 'Data' + datetime.date.today().strftime("%Y%m%d") else: outputData = os.path.dirname(inputData) + os.sep + 'Data' + os.sep + 'Data' + datetime.date.today().strftime("%Y%m%d") if args.coeff is not None: if len(args.coeff) % 2 == 0: for i in range(0, len(args.coeff), 2): if i <= len(args.coeff): coefficients[str(args.coeff[i])]=float(args.coeff[i+1]) if args.ext is not None: extension = args.ext print('Loading from: ' + inputData) print('Saving to: ' + outputData) print('Coefficients are: ', end='') pprint(coefficients) if os.path.isdir(inputData): print('extension is: ' + extension +'\nStarting.... \n') TranslocationEvents = batcheventdetection(inputData, extension, newcoefficients=coefficients, force=args.force, cut=args.cut) else: print('Starting.... \n') TranslocationEvents = eventdetection(inputData,coefficients, args.cut) #Check if list is empty if TranslocationEvents.events: LoadData.SaveVariables(outputData, TranslocationEvents=TranslocationEvents) diff --git a/EventDetectorUI.py b/EventDetectorUI.py index 0682fb4..21bef93 100644 --- a/EventDetectorUI.py +++ b/EventDetectorUI.py @@ -1,261 +1,291 @@ import sys import os from PyQt5.QtWidgets import * from PyQt5 import QtCore +from PyQt5.QtGui import QColor import matplotlib matplotlib.use('QT5Agg') import numpy as np import matplotlib.pylab as plt from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar font = {'family' : 'monospace', 'weight' : 'regular', 'size' : 4} matplotlib.rc('font', **font) # pass in the font dict as kwargs +from EventDetection import * class AnalysisUI(QWidget): def __init__(self): super().__init__() self.initUI() def AllVariable(self): # Let's dump all the variable we will need here... self.importedFiles = [] self.selectedFiles = [] self.analyzedFiles = [] # Defining the plots self.TurnToolbarsOn = False self.fig_singleEvent = plt.figure(1, figsize=(10, 10)) self.ax_singleEvent = self.fig_singleEvent.add_subplot(111) self.ax_singleEvent.plot(np.arange(1, 10), np.arange(1, 10)) self.fig_wholeTrace = plt.figure(2, figsize=(16, 9)) self.ax_wholeTrace = self.fig_wholeTrace.add_subplot(111) self.ax_wholeTrace.plot(np.arange(1, 10), -np.arange(1, 10)) self.figure_singleEvent = FigureCanvas(figure=self.fig_singleEvent) self.figure_wholeTrace = FigureCanvas(figure=self.fig_wholeTrace) if self.TurnToolbarsOn: self.toolbarWholeTrace = NavigationToolbar(self.figure_wholeTrace, self) self.toolbarSingleEvent = NavigationToolbar(self.figure_singleEvent, self) def initUI(self): # Main Window self.AllVariable() self.setGeometry(0, 0, 1200, 750) self.setWindowTitle('Do not use this tool for shitty science...') # Assemble the different layout pieces self.MakeFileImportLayout() self.MakeAnalysisParametersAndRunLayout() self.MakeEventNavigatorLayout() windowLayout = QHBoxLayout() windowLayout.addWidget(self.FileImportLayout, 10) windowLayout.addWidget(self.AnalysisParameters) windowLayout.addWidget(self.EventNavigatorLayout) self.setLayout(windowLayout) ## All Actions go here: self.button_fileimport.clicked.connect(self.ImportButtonPushed) self.button_clearfilelist.clicked.connect(self.ClearListButtonPushed) self.list_filelist.clicked.connect(self.SelectionInFileListChanged) self.button_startAnalysis.clicked.connect(self.AnalysisButtonPressed) self.show() def MakeFileImportLayout(self): self.FileImportLayout = QGroupBox("File Import") self.FileImportLayout.setFixedWidth(400) layout = QGridLayout() self.button_fileimport = QPushButton('Open Files') self.button_fileimport.setToolTip('Select the files you want to import into the list. You can do multiple selections.') self.button_clearfilelist = QPushButton('Clear List') self.button_clearfilelist.setToolTip('This deletes your current list of files.') self.list_filelist = QListWidget() self.list_filelist.setSelectionMode(QAbstractItemView.ExtendedSelection) self.list_filelist.setToolTip('This is the list of current files. Entries in green are already analyzed. Your selection defines which files are used in the analysis.') layout.addWidget(self.button_fileimport, 0, 0) layout.addWidget(self.list_filelist, 1, 0) layout.addWidget(self.button_clearfilelist, 2, 0) self.FileImportLayout.setLayout(layout) def MakeAnalysisParametersAndRunLayout(self): self.AnalysisParameters = QGroupBox("Analysis") self.LowPassSettings = QGroupBox("Low Pass Settings") self.CusumSettings = QGroupBox("Cusum Settings") self.CusumSettings.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum)) #This allows this part to remain small in vertical position when the UI resizes. self.OtherSettings = QGroupBox("Other Settings") self.button_startAnalysis = QPushButton('Start Analysis') self.button_startAnalysis.setToolTip('This button will provoke your computer to sel-destruct. Use at your own risk!!!!!') ## Low Pass Settings self.settings_LP_a = QComboBox() availableoptions = [0.99, 0.999, 0.9999] for i in availableoptions: self.settings_LP_a.addItem(str(i)) self.settings_LP_a.setToolTip('How strong should the filter be? Higher number is stronger filtering, which means less found events...') ## EndValueE self.settings_LP_E = QDoubleSpinBox() self.settings_LP_E.setValue(0) self.settings_LP_E.setSingleStep(0.1) self.settings_LP_E.setToolTip('The current needs to go above mean + E*std to terminate the event') ## StartValueS self.settings_LP_S = QDoubleSpinBox() self.settings_LP_S.setValue(5) self.settings_LP_S.setSingleStep(0.1) self.settings_LP_S.setToolTip('The current needs to go below mean + S*std to initiate an event') layout1 = QGridLayout() layout1.addWidget(QLabel('Low Pass Filter Coefficient (a)'), 0, 0) layout1.addWidget(self.settings_LP_a, 0, 1) layout1.addWidget(QLabel('Start Coefficient (S)'), 1, 0) layout1.addWidget(self.settings_LP_S, 1, 1) layout1.addWidget(QLabel('End Coefficient (E)'), 3, 0) layout1.addWidget(self.settings_LP_E, 3, 1) self.LowPassSettings.setLayout(layout1) ##Cusum Settings self.settings_cusum_hBook = QDoubleSpinBox() self.settings_cusum_hBook.setValue(1) self.settings_cusum_hBook.setSingleStep(0.1) self.settings_cusum_hBook.setToolTip('Some random stupid parameter') self.settings_cusum_dI = QDoubleSpinBox() self.settings_cusum_dI.setValue(1) self.settings_cusum_dI.setSuffix(' nA') self.settings_cusum_dI.setSingleStep(0.1) self.settings_cusum_dI.setToolTip('The most probable current drop') layoutCusum = QGridLayout() layoutCusum.addWidget(QLabel('h-Book'), 0, 0) layoutCusum.addWidget(self.settings_cusum_hBook, 0, 1) layoutCusum.addWidget(QLabel('Delta I'), 1, 0) layoutCusum.addWidget(self.settings_cusum_dI, 1, 1) self.CusumSettings.setLayout(layoutCusum) ## Other Settings self.settings_other_ChimeraLP = QDoubleSpinBox() self.settings_other_ChimeraLP.setValue(10) self.settings_other_ChimeraLP.setSingleStep(10) self.settings_other_ChimeraLP.setSuffix(' kHz') self.settings_other_ChimeraLP.setToolTip('Low pass setting for high-bandwidth Chimera Data') self.settings_other_MaxEventLength = QDoubleSpinBox() self.settings_other_MaxEventLength.setValue(0.2) self.settings_other_MaxEventLength.setSingleStep(0.05) self.settings_other_MaxEventLength.setSuffix(' s') self.settings_other_MaxEventLength.setToolTip('maximal event length to be considered an event') self.settings_other_MinEventLength = QDoubleSpinBox() self.settings_other_MinEventLength.setValue(600) self.settings_other_MinEventLength.setSingleStep(100) self.settings_other_MinEventLength.setSuffix(' us') self.settings_other_MinEventLength.setToolTip('maximal event length to be considered an impulse') self.settings_other_fitLength = QDoubleSpinBox() self.settings_other_fitLength.setValue(3) self.settings_other_fitLength.setSingleStep(0.5) self.settings_other_fitLength.setSuffix(' ms') self.settings_other_fitLength.setToolTip('minimal event length to be fitted for impulses') layoutOther = QGridLayout() layoutOther.addWidget(QLabel('Chimera Low Pass'), 0, 0) layoutOther.addWidget(self.settings_other_ChimeraLP, 0, 1) layoutOther.addWidget(QLabel('Max event length'), 1, 0) layoutOther.addWidget(self.settings_other_MaxEventLength, 1, 1) layoutOther.addWidget(QLabel('Min event length'), 2, 0) layoutOther.addWidget(self.settings_other_MinEventLength, 2, 1) layoutOther.addWidget(QLabel('Fit length'), 3, 0) layoutOther.addWidget(self.settings_other_fitLength, 3, 1) self.OtherSettings.setLayout(layoutOther) ##Final Assembly layout = QGridLayout() layout.addWidget(self.LowPassSettings, 0, 0) layout.addWidget(self.CusumSettings, 1, 0) layout.addWidget(self.OtherSettings, 2, 0) layout.addWidget(self.button_startAnalysis, 3, 0) self.AnalysisParameters.setLayout(layout) def MakeEventNavigatorLayout(self): self.EventNavigatorLayout = QGroupBox("Event Navigator") self.navigation_buttons =QButtonGroup() self.button_eventForward = QPushButton('Forward') self.button_eventForward.setToolTip('Advance one event forward') self.button_eventBackward = QPushButton('Backward') self.button_eventBackward.setToolTip('Go one event back') self.text_eventNumber = QLineEdit('Current Event Number') self.text_eventNumber.setToolTip('This is the number of the current event. You can edit this field to jump directly to an event.') self.text_eventNumber.setAlignment(QtCore.Qt.AlignCenter) layout = QGridLayout() layout.addWidget(self.figure_singleEvent, 0, 0, 1, 3) layout.addWidget(self.button_eventForward, 2, 2) layout.addWidget(self.text_eventNumber, 2, 1) layout.addWidget(self.button_eventBackward, 2, 0) layout.addWidget(self.figure_wholeTrace, 3, 0, 1, 3) if self.TurnToolbarsOn: layout.addWidget(self.toolbarSingleEvent, 1, 0, 1, 3) layout.addWidget(self.toolbarWholeTrace, 4, 0, 1, 3) self.EventNavigatorLayout.setLayout(layout) ## Button Actions def ImportButtonPushed(self, event): files = QFileDialog.getOpenFileNames(self, 'Select Files to Add to list', filter = "Images (*.dat *.log)") ## Add to file list if unique: for i in files[0]: if i not in self.importedFiles: self.importedFiles.append(i) self.UpdateFileList() def ClearListButtonPushed(self, event): self.importedFiles = [] self.UpdateFileList() def UpdateFileList(self): self.list_filelist.clear() - for i in self.importedFiles: + for ind, i in enumerate(self.importedFiles): self.list_filelist.addItem(os.path.split(i)[1]) - ## If file is present, make the row green. This means analysis was done on it. + folder = os.path.dirname(i) + os.sep + 'analysisfiles' + filename, file_extension = os.path.splitext(os.path.basename(i)) + potentialanalysisfile = folder + os.sep + filename + 'data.db' + print(potentialanalysisfile) + print(os.path.exists(potentialanalysisfile)) + if os.path.isfile(potentialanalysisfile): + ## If file is present, make the row green. This means analysis was done on it. + self.list_filelist.item(ind).setBackground(QColor('green')) def SelectionInFileListChanged(self, event): items = self.list_filelist.selectedItems() x = [] for i in range(len(items)): x.append(str(self.list_filelist.selectedItems()[i].text())) self.selectedFiles = x print('The following files are selected in the list:') print(x) def AnalysisButtonPressed(self): - self.analyzedFiles = self.selectedFiles - + # Get Full File Paths: + fullfilepaths = [] + for i in self.selectedFiles: + for j in self.importedFiles: + if i in j: + fullfilepaths.append(j) + print(fullfilepaths) + # Fill coefficient dictionary from the user inputs + coefficients = {} + coefficients['maxEventLength'] = self.settings_other_MaxEventLength.value() + coefficients['minEventLength'] = self.settings_other_MinEventLength.value() + coefficients['fitLength'] = self.settings_other_fitLength.value() + coefficients['delta'] = self.settings_cusum_dI.value() + coefficients['hbook'] = self.settings_cusum_hBook.value() + coefficients['a'] = np.float(self.settings_LP_a.currentText()) + coefficients['S'] = self.settings_LP_S.value() + coefficients['E'] = self.settings_LP_E.value() + coefficients['ChimeraLowPass'] = self.settings_other_ChimeraLP.value() + coefficients['dt'] = 25 + coefficients['deltaRel'] = None + for i in fullfilepaths: + eventdetectionwithfilegeneration(i, coefficients, forceRun=True, verboseLevel=1) + self.UpdateFileList() def closeEvent(self, event): ''' reply = QMessageBox.question(self, 'Message', "Are you sure to quit? Don't make me sad... :( :( :(", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: event.accept() else: event.ignore() ''' ## Execution if __name__ == '__main__': app = QApplication(sys.argv) ex = AnalysisUI() sys.exit(app.exec_())