class LabeledFloatEntry(Frame): def __init__(self, parent, label, entry='', separator=': ', label_width=20, entry_width=60, label_anchor=Tkinter.E, show='', isArray=False, returnCallback=None, *args, **kw): apply(Frame.__init__, (self, parent) + args, kw) self.grid_columnconfigure(1, weight=1) self.separator = separator text = label + separator self.label = Label(self, text=text, width=label_width, anchor=label_anchor) self.label.grid(row=0, column=0, sticky=Tkinter.EW) self.entry = FloatEntry(self, show=show, width=entry_width, isArray=isArray, returnCallback=returnCallback) self.entry.grid(row=0, column=1, sticky=Tkinter.EW) self.setEntry(entry) def getLabel(self): text = self.label.get() n = text.find(self.separator) if (n >= 0): text = text[:n] return text def setLabel(self, text=''): text = text + self.separator self.label.set(text) def getEntry(self): return self.entry.get() def setEntry(self, value=''): self.entry.set(value)
class CalcHeteroNoePopup(BasePopup): """ **Calculate Heteronuclear NOE Values From Peak Intensities** The purpose of this popup window is to calculate the heteronuclear NOE for amide resonances based upon a comparison of the peak intensities in spectra that derive from an NOE saturated experiment and an unsaturated (reference) experiment. The basic idea of this tool is that three peak lists are chosen, two of which are for heteronuclear NOE experiments (H,N axes); unsaturated reference and saturated, and one which is the source of assignments and peak locations. This last "Assignment" peak list may be the same as one of the NOE peak lists, but may also be entirely separate. The "Assignment" peak list is used to specify which peak assignments and locations should be used for the calculation of the heteronuclear NOE values, and thus can be used to specify only a subset of the resonances for measurement. For example, it is common to copy an HQSC peak list for use as the "Assignment" peak list but remove overlapped and NH2 peaks so that the NOE values are only calculated for separated backbone amides. The calculation process involves taking each of these assigned peaks and finding peaks with the same assignment in the NOE peak lists, or if that fails finding peaks with a similar position (within the stated tolerances); new peaks may be picked if the "Pick new peaks?" option is set. The first "Peak Lists & Settings" tab allows the user to choose the peak lists and various options that will be used in the peak-finding and NOE calculation. The "Peaks" table allows the peaks from each of the three peak list selections to be displayed when one of the "Show" buttons is clicked. The [Separate Peak Table] function allows these peaks to be displayed in the main, separate `Peak Lists`_ table, which has many more peak manipulation options. The options below the table may be used to locate selected peaks within the spectrum window displays. The second "Peak Intensity Comparison" tab is where the heteronuclear NOE values are actually calculated. Assuming that two NOE experiment peak lists have been chosen and that some of their peaks match the assigned peak positions then the peak intensities are extracted and NOE values automatically calculated when the tab is opened. Although, a refresh of the table can be forced with [Find Matching Peaks] at the bottom If pairs of NOE saturated and reference peaks are found then the actual heteronuclear NOE value is displayed as the "Intensity Ratio" in the last, rightmost, column of the table. To store these values as a NOE measurement list; so that the data can be saved in the CCPN project without need for recalculation, the [Create Hetero NOE List] function can be used. The results are then available to view at any time via the `Measurement Lists`_ table. **Caveats & Tips** Erroneous peak intensity comparisons may be removed with the [Remove Pairs] function, but its is common to curate the "Assign" peak list first and avoid tidying afterwards. The "Closeness score" can be used to find peak positions where the compared NOE peaks are unexpectedly far from one another. .. _`Peak Lists`: EditPeakListsPopup.html .. _`Measurement Lists`: EditMeasurementListsPopup.html """ def __init__(self, parent, *args, **kw): self.guiParent = parent self.peakPairs = [] self.intensityType = 'height' self.selectedPair = None self.assignPeakList = None self.refPeakList = None self.satPeakList = None self.displayPeakList = None self.waiting = 0 BasePopup.__init__(self, parent, title="Data Analysis : Heteronuclear NOE", **kw) def body(self, guiFrame): self.geometry('700x700') guiFrame.expandGrid(0,0) options = ['Peak Lists & Settings','Peak Intensity Comparison'] tabbedFrame = TabbedFrame(guiFrame, options=options, callback=self.changeTab) tabbedFrame.grid(row=0, column=0, sticky='nsew') self.tabbedFrame = tabbedFrame frameA, frameB = tabbedFrame.frames row = 0 frameA.grid_columnconfigure(1, weight=1) frameA.grid_columnconfigure(3, weight=1) frameA.grid_columnconfigure(5, weight=1) frameA.grid_rowconfigure(5, weight=1) tipText = 'Number of reference peaks (no saturation)' self.peaksALabel = Label(frameA, text='Number of Ref Peaks: ', tipText=tipText) self.peaksALabel.grid(row=1,column=0,columnspan=2,sticky='w') tipText = 'Number of NOE saturation peaks' self.peaksBLabel = Label(frameA, text='Number of Sat Peaks: ', tipText=tipText) self.peaksBLabel.grid(row=1,column=2,columnspan=2,sticky='w') tipText = 'Number of peaks in assigned list' self.peaksCLabel = Label(frameA, text='Number of Assign Peaks: ', tipText=tipText) self.peaksCLabel.grid(row=1,column=4,columnspan=2,sticky='w') tipText = 'Selects which peak list is considered the NOE intensity reference (no saturation)' specALabel = Label(frameA, text='Ref Peak List: ') specALabel.grid(row=0,column=0,sticky='w') self.specAPulldown = PulldownList(frameA, callback=self.setRefPeakList, tipText=tipText) self.specAPulldown.grid(row=0,column=1,sticky='w') tipText = 'Selects which peak list is considered as NOE saturated.' specBLabel = Label(frameA, text='Sat Peak List: ') specBLabel.grid(row=0,column=2,sticky='w') self.specBPulldown = PulldownList(frameA, callback=self.setSatPeakList, tipText=tipText) self.specBPulldown.grid(row=0,column=3,sticky='w') tipText = 'Selects a peak list with assignments to use as a positional reference' specCLabel = Label(frameA, text='Assignment Peak List: ') specCLabel.grid(row=0,column=4,sticky='w') self.specCPulldown = PulldownList(frameA, callback=self.setAssignPeakList, tipText=tipText) self.specCPulldown.grid(row=0,column=5,sticky='w') frame0a = Frame(frameA) frame0a.grid(row=2,column=0,columnspan=6,sticky='nsew') frame0a.grid_columnconfigure(9, weight=1) tipText = '1H ppm tolerance for matching assigned peaks to reference & NOE saturation peaks' tolHLabel = Label(frame0a, text='Tolerances: 1H') tolHLabel.grid(row=0,column=0,sticky='w') self.tolHEntry = FloatEntry(frame0a,text='0.02', width=6, tipText=tipText) self.tolHEntry .grid(row=0,column=1,sticky='w') tipText = '15N ppm tolerance for matching assigned peaks to reference & NOE saturation peaks' tolNLabel = Label(frame0a, text=' 15N') tolNLabel .grid(row=0,column=2,sticky='w') self.tolNEntry = FloatEntry(frame0a,text='0.1', width=6, tipText=tipText) self.tolNEntry .grid(row=0,column=3,sticky='w') tipText = 'Whether to peak new peaks in reference & NOE saturated lists (at assignment locations)' label = Label(frame0a, text=' Pick new peaks?', grid=(0,4)) self.pickPeaksSelect = CheckButton(frame0a, tipText=tipText, grid=(0,5), selected=True) tipText = 'Whether to assign peaks in the peaks in the reference & NOE saturation lists, if not already assigned' label = Label(frame0a, text=' Assign peaks?') label.grid(row=0,column=6,sticky='w') self.assignSelect = CheckButton(frame0a, tipText=tipText) self.assignSelect.set(1) self.assignSelect.grid(row=0,column=7,sticky='w') tipText = 'Whether to consider peak height or volume in the heteronuclear NOE calculation' intensLabel = Label(frame0a, text=' Intensity Type:') intensLabel .grid(row=0,column=8,sticky='w') self.intensPulldown = PulldownList(frame0a, texts=['height','volume'], callback=self.setIntensityType, tipText=tipText) self.intensPulldown.grid(row=0,column=9,sticky='w') divider = LabelDivider(frameA, text='Peaks', grid=(3,0), gridSpan=(1,6)) tipTexts = ['Show the selected intensity reference peaks in the below table', 'Show the selected NOE saturation peaks in the below table', 'Show the selected assigned peak list in the below table', 'Show the displayed peaks in a separate peak table, where assignments etc. may be adjusted'] texts = ['Show Ref Peaks','Show Sat Peaks', 'Show Assign Peaks', 'Separate Peak Table'] commands = [self.viewRefPeakList, self.viewSatPeakList, self.viewAssignPeakList, self.viewSeparatePeakTable] self.viewPeaksButtons = ButtonList(frameA, expands=True, tipTexts=tipTexts, texts=texts, commands=commands) self.viewPeaksButtons.grid(row=4,column=0,columnspan=6,sticky='nsew') self.peakTable = PeakTableFrame(frameA, self.guiParent, grid=(5,0), gridSpan=(1,6)) self.peakTable.bottomButtons1.grid_forget() self.peakTable.bottomButtons2.grid_forget() #self.peakTable.topFrame.grid_forget() self.peakTable.topFrame.grid(row=2, column=0, sticky='ew') # Next tab frameB.expandGrid(0,0) tipTexts = ['Row number', 'Assignment annotation for NOE saturation peak', 'Assignment annotation for reference peak (no saturation)', '1H chemical shift of NOE saturation peak', '1H chemical shift of reference peak', '15N chemical shift of NOE saturation peak', '15N chemical shift of reference peak', 'The separation between compared peaks: square root of the sum of ppm differences squared', 'The intensity if the NOE saturation peak', 'The intensity of the reference peak (no saturation)', 'Ratio of peak intensities: saturated over reference', 'Residue(s) for reference peak'] colHeadings = ['#','Sat Peak','Ref Peak','1H shift A', '1H shift B','15N shift A','15N shift B', 'Closeness\nScore','Intensity A','Intensity B', 'Intensity\nRatio','Residue'] self.scrolledMatrix = ScrolledMatrix(frameB, multiSelect=True, headingList=colHeadings, callback=self.selectCell, tipTexts=tipTexts, grid=(0,0), deleteFunc=self.removePair) tipTexts = ['Force a manual update of the table; pair-up NOE saturation and reference peaks according to assigned peak positions', 'Remove the selected rows of peak pairs', 'Show peaks corresponding to the selected row in a table', 'Save the Heteronuclear NOE values in the CCPN project as a data list'] texts = ['Refresh Table','Remove Pairs', 'Show Peak Pair','Create Hetero NOE List'] commands = [self.matchPeaks,self.removePair, self.showPeakPair,self.makeNoeList] self.pairButtons = ButtonList(frameB, tipTexts=tipTexts, grid=(1,0), texts=texts, commands=commands) bottomButtons = UtilityButtonList(tabbedFrame.sideFrame, helpUrl=self.help_url) bottomButtons.grid(row=0, column=0, sticky='e') self.updatePulldowns() self.updateAfter() self.administerNotifiers(self.registerNotify) def administerNotifiers(self, notifyFunc): for func in ('__init__', 'delete','setName'): for clazz in ('ccp.nmr.Nmr.DataSource', 'ccp.nmr.Nmr.Experiment',): notifyFunc(self.updatePulldowns, clazz, func) for func in ('__init__', 'delete'): notifyFunc(self.updatePulldowns,'ccp.nmr.Nmr.PeakList', func) for func in ('__init__', 'delete','setAnnotation','setFigOfMerit'): notifyFunc(self.updatePeaks, 'ccp.nmr.Nmr.Peak', func) for func in ('setAnnotation','setPosition','setNumAliasing'): notifyFunc(self.updatePeakChild, 'ccp.nmr.Nmr.PeakDim', func) for func in ('__init__', 'delete', 'setValue'): notifyFunc(self.updatePeakChild, 'ccp.nmr.Nmr.PeakIntensity', func) def changeTab(self, index): if index == 1: self.matchPeaks() def open(self): self.updatePulldowns() self.updateAfter() BasePopup.open(self) def destroy(self): self.administerNotifiers(self.unregisterNotify) BasePopup.destroy(self) def updatePulldowns(self, *obj): index0 = 0 index1 = 0 index2 = 0 names, peakLists = self.getPeakLists() if names: if self.refPeakList not in peakLists: self.refPeakList = peakLists[0] if self.satPeakList not in peakLists: self.satPeakList = peakLists[0] if self.assignPeakList not in peakLists: self.assignPeakList = peakLists[0] index0 = peakLists.index(self.refPeakList) index1 = peakLists.index(self.satPeakList) index2 = peakLists.index(self.assignPeakList) self.specAPulldown.setup(names, peakLists, index0) self.specBPulldown.setup(names, peakLists, index1) self.specCPulldown.setup(names, peakLists, index2) def updatePeakChild(self,peakChild): if self.waiting: return self.updatePeaks(peakChild.peak) def updatePeaks(self, peak): if self.waiting: return if peak.peakList in (self.refPeakList,self.satPeakList,self.assignPeakList): if peak.isDeleted and (peak.peakList in (self.refPeakList,self.satPeakList) ): for peaks in self.peakPairs: if peak in peaks: self.peakPairs.remove(peaks) if self.selectedPair is peaks: self.selectedPair = None self.updateAfter() return self.updateAfter() def setIntensityType(self, intensityType): self.intensityType = intensityType self.updateAfter() def viewRefPeakList(self): if self.refPeakList: self.updatePeakTable(self.refPeakList) def viewSatPeakList(self): if self.satPeakList: self.updatePeakTable(self.satPeakList) def viewAssignPeakList(self): if self.assignPeakList: self.updatePeakTable(self.assignPeakList) def viewSeparatePeakTable(self): if self.displayPeakList: self.guiParent.editPeakList(peakList=self.displayPeakList) def setRefPeakList(self, refPeakList): if self.displayPeakList is self.refPeakList: self.updatePeakTable(refPeakList) self.refPeakList = refPeakList self.updateViewButtons() self.updateAfter() def setSatPeakList(self, satPeakList): if self.displayPeakList is self.satPeakList: self.updatePeakTable(satPeakList) self.satPeakList = satPeakList self.updateViewButtons() self.updateAfter() def setAssignPeakList(self, assignPeakList): if self.displayPeakList is self.assignPeakList: self.updatePeakTable(assignPeakList) self.assignPeakList = assignPeakList self.updateViewButtons() self.updateAfter() def getPeakListName(self, peakList): if peakList: spectrum = peakList.dataSource experiment = spectrum.experiment name = '%s:%s:%d' % (experiment.name, spectrum.name, peakList.serial) else: name = '<None>' return name def getPeakLists(self): names = [] peakLists = [] for experiment in self.nmrProject.sortedExperiments(): for dataSource in experiment.sortedDataSources(): if dataSource.numDim == 2: dimsN = findSpectrumDimsByIsotope(dataSource,'15N') dimsH = findSpectrumDimsByIsotope(dataSource,'1H') if len(dimsN) == 1 and len(dimsH) == 1: for peakList in dataSource.sortedPeakLists(): name = self.getPeakListName(peakList) names.append( name ) peakLists.append(peakList) return names, peakLists def showPeakPair(self): if self.selectedPair: self.guiParent.viewPeaks(self.selectedPair) def selectCell(self, object, row, col): self.selectedPair = object if self.selectedPair: self.pairButtons.buttons[1].enable() self.pairButtons.buttons[2].enable() else: self.pairButtons.buttons[1].disable() self.pairButtons.buttons[2].disable() def removePair(self, *event): pairs = self.scrolledMatrix.currentObjects if pairs: for pair in pairs: self.peakPairs.remove(pair) self.selectedPair = None self.updateAfter() def matchPeaks(self): # assign relative to reference if self.assignPeakList and self.assignPeakList.peaks and self.refPeakList and self.satPeakList: tolH = float( self.tolHEntry.get() ) tolN = float( self.tolNEntry.get() ) pickNewPeaks = self.pickPeaksSelect.get() doAssign = self.assignSelect.get() dimH = findSpectrumDimsByIsotope(self.assignPeakList.dataSource,'1H' )[0] dimHA = findSpectrumDimsByIsotope(self.refPeakList.dataSource,'1H' )[0] dimHB = findSpectrumDimsByIsotope(self.satPeakList.dataSource,'1H' )[0] dimN = 1-dimH dimNA = 1-dimHA dimNB = 1-dimHB tolerancesA = [0,0] tolerancesA[dimHA] = tolH tolerancesA[dimNA] = tolN tolerancesB = [0,0] tolerancesB[dimHB] = tolH tolerancesB[dimNB] = tolN self.peakPairs = matchHnoePeaks(self.assignPeakList,self.refPeakList, self.satPeakList,tolerancesA,tolerancesB, pickNewPeaks,doAssign) self.updateAfter() def makeNoeList(self): if self.refPeakList is self.satPeakList: showWarning('Same Peak List', 'Ref Peak List and Sat Peak List cannot be the same', parent=self) return if self.peakPairs: s1 = self.refPeakList.dataSource s2 = self.satPeakList.dataSource noiseRef = getSpectrumNoise(s1) noiseSat = getSpectrumNoise(s2) es = '%s-%s' % (s1.experiment.name,s2.experiment.name) if len(es) > 50: es = 'Expt(%d)-Expt(%d)' % (s1.experiment.serial,s2.experiment.serial) noeList = self.nmrProject.newNoeList(unit='None',name='Hetero NOE list for %s' % es) noeList.setExperiments([s1.experiment,]) if s1.experiment is not s2.experiment: noeList.addExperiment( s2.experiment ) # TBD: sf, noeValueType, refValue, refDescription resonancePairsSeen = set() for (peakA,peakB) in self.peakPairs: # peakA is sat intensA = getPeakIntensity(peakA,self.intensityType) intensB = getPeakIntensity(peakB,self.intensityType) value = float(intensA)/intensB error = abs(value) * sqrt((noiseSat/intensA)**2 + (noiseRef/intensB)**2) resonances = tuple(self.getPeakResonances(peakA)) frozenResonances = frozenset(resonances) if len(resonances) < 2: pl = peakA.peakList sp = pl.dataSource msg = 'Skipping %s:%s:%d peak %d it has too few resonances assigned' data = (sp.experiment.name, sp.name, pl.serial, peakA.serial) showWarning('Warning',msg % data, parent=self) elif len(resonances) > 2: pl = peakA.peakList sp = pl.dataSource resonanceText = ' '.join([makeResonanceGuiName(r) for r in resonances]) msg = 'Skipping %s:%s:%d peak %d it has too many resonances assigned (%s)' data = (sp.experiment.name, sp.name, pl.serial, peakA.serial, resonanceText) showWarning('Warning', msg % data, parent=self) elif frozenResonances not in resonancePairsSeen: resonancePairsSeen.add(frozenResonances) noeList.newNoe(value=value,resonances=resonances,peaks=[peakA,peakB],error=error) else: resonanceText = ' '.join([makeResonanceGuiName(r) for r in resonances]) msg = 'Skipping duplicate entry for resonances %s' % resonanceText showWarning('Warning', msg, parent=self) self.parent.editMeasurements(measurementList=noeList) def getPeakResonances(self,peak): resonances = [] for peakDim in peak.sortedPeakDims(): for contrib in peakDim.sortedPeakDimContribs(): resonances.append(contrib.resonance) return resonances def updateAfter(self, *opt): if self.waiting: return else: self.waiting = True self.after_idle(self.update) def updateViewButtons(self): if self.refPeakList: self.viewPeaksButtons.buttons[0].enable() else: self.viewPeaksButtons.buttons[0].disable() if self.satPeakList: self.viewPeaksButtons.buttons[1].enable() else: self.viewPeaksButtons.buttons[1].disable() if self.assignPeakList: self.viewPeaksButtons.buttons[2].enable() else: self.viewPeaksButtons.buttons[2].disable() def updatePeakTable(self, peakList): if peakList is not self.displayPeakList: self.displayPeakList = peakList self.peakTable.update(peaks=peakList.sortedPeaks()) def update(self): if self.refPeakList: self.peaksALabel.set( 'Number of Ref Peaks: %d' % len(self.refPeakList.peaks) ) else: self.peaksALabel.set( 'Number of Ref Peaks: %d' % 0 ) if self.satPeakList: self.peaksBLabel.set( 'Number of Sat Peaks: %d' % len(self.satPeakList.peaks) ) else: self.peaksBLabel.set( 'Number of Sat Peaks: %d' % 0 ) if self.assignPeakList: self.peaksCLabel.set( 'Number of Assign Peaks: %d' % len(self.assignPeakList.peaks) ) else: self.peaksCLabel.set( 'Number of Assign Peaks: %d' % 0 ) if self.refPeakList and self.satPeakList and self.assignPeakList: if self.refPeakList is self.satPeakList: self.pairButtons.buttons[0].disable() else: self.pairButtons.buttons[0].enable() else: self.pairButtons.buttons[0].disable() if self.selectedPair: self.pairButtons.buttons[1].enable() self.pairButtons.buttons[2].enable() else: self.pairButtons.buttons[1].disable() self.pairButtons.buttons[2].disable() if self.peakPairs: self.pairButtons.buttons[3].enable() dsA = self.peakPairs[0][0].peakList.dataSource dsB = self.peakPairs[0][1].peakList.dataSource dimHA = findSpectrumDimsByIsotope(dsA,'1H')[0] dimHB = findSpectrumDimsByIsotope(dsB,'1H')[0] dimNA = findSpectrumDimsByIsotope(dsA,'15N')[0] dimNB = findSpectrumDimsByIsotope(dsB,'15N')[0] else: self.pairButtons.buttons[3].disable() objectList = [] textMatrix = [] i = 0 for (peakA,peakB) in self.peakPairs: i += 1 peakDimsA = peakA.sortedPeakDims() peakDimsB = peakB.sortedPeakDims() ppm0 = peakDimsA[dimHA].value ppm1 = peakDimsB[dimHB].value ppm2 = peakDimsA[dimNA].value ppm3 = peakDimsB[dimNB].value d0 = abs(ppm0-ppm1) d1 = abs(ppm2-ppm3) intensA = getPeakIntensity(peakA,self.intensityType) intensB = getPeakIntensity(peakB,self.intensityType) datum = [] datum.append( i ) datum.append( getPeakAnnotation(peakA, doPeakDims=False) ) datum.append( getPeakAnnotation(peakB, doPeakDims=False) ) datum.append( ppm0 ) datum.append( ppm1 ) datum.append( ppm2 ) datum.append( ppm3 ) datum.append( sqrt((d0*d0)+(d1*d1)) ) datum.append( intensA ) datum.append( intensB ) if intensB: datum.append( float(intensA)/intensB ) else: datum.append( None ) seqCodes = ','.join(['%s' % seqCode for seqCode in getPeakSeqCodes(peakB)]) datum.append(seqCodes) objectList.append( (peakA,peakB) ) textMatrix.append( datum ) if not objectList: textMatrix.append([]) self.scrolledMatrix.update(objectList=objectList, textMatrix=textMatrix) self.waiting = False
class AuremolFrame(Frame): def __init__(self, guiParent, ccpnProject=None, **kw): self.guiParent = guiParent self.project = ccpnProject self.spectrum = None self.peakMode = 0 if ccpnProject: self.nmrProject = ccpnProject.currentNmrProject else: self.nmrProject = None Frame.__init__(self, guiParent, **kw) self.expandGrid(0,0) options = ['Peak Picking',] #,'About Auremol' 'NOE assignment','Homology Modelling',] self.tabbedFrame = TabbedFrame(self, options=options) self.tabbedFrame.grid(row=0,column=0,sticky='nsew') frameA = self.tabbedFrame.frames[0] #frameC.grid_columnconfigure(0, weight=1) #frameC.grid_rowconfigure(0, weight=1) #frameD.grid_columnconfigure(0, weight=1) #frameD.grid_rowconfigure(0, weight=1) # # Frame A # frameA.expandGrid(2,0) frameA.expandGrid(3,0) frameA.expandGrid(4,0) frameA.expandGrid(5,0) frame = Frame(frameA, grid=(0,0)) frame.expandGrid(0,4) label = Label(frame, text='Spectrum:', grid=(0,0)) self.spectrumPulldown = PulldownList(frame, self.changeSpectrum, grid=(0,1)) label = Label(frame, text=' Use Peak Sign:', grid=(0,2)) self.peakModePulldown = PulldownList(frame, self.changePeakMode, texts=PEAK_MODES, objects=[0,1,2], grid=(0,3)) frame = Frame(frameA, grid=(1,0)) frame.expandGrid(0,4) label = Label(frame, text='Integration Depth (Relative to max):', grid=(1,0)) self.segLevelEntry = FloatEntry(frame, text=0.1, grid=(1,1), width=8) label = Label(frame, text='Threshold (Threshold only):', grid=(1,3)) self.thresholdEntry = IntEntry(frame, text=100000, grid=(1,4), width=8) label = Label(frame, text='Keep Peaks (Adaptive only):', grid=(1,5)) self.keepPeakEntry = IntEntry(frame, text=4000, grid=(1,6), width=8) texts = ['Threshold\nPeak Pick','Adaptive\nPeak Pick'] commands = [self.pickThreshold, self.pickAdaptive] self.buttons = ButtonList(frameA, texts=texts, commands=commands, grid=(2,0), sticky='NSEW') frame = Frame(frameA, grid=(3,0)) frame.expandGrid(0,0) frame = Frame(frameA, grid=(4,0)) frame.expandGrid(0,0) frame = Frame(frameA, grid=(5,0)) frame.expandGrid(0,0) # # About """ frameB.expandGrid(4,0) label = Label(frameB, text='References', font='Helvetica 12 bold') label.grid(row=0, column=0, sticky='w') text = * Gronwald W, Brunner K, Kirchhofer R, Nasser A, Trenner J, Ganslmeier B, Riepl H, Ried A, Scheiber J, Elsner R, Neidig K-P, Kalbitzer HR AUREMOL, a New Program for the Automated Structure Elucidation of Biological Macromolecules Bruker Reports 2004; 154/155: 11-14 * Ried A, Gronwald W, Trenner JM, Brunner K, Neidig KP, Kalbitzer HR Improved simulation of NOESY spectra by RELAX-JT2 including effects of J-coupling, transverse relaxation and chemical shift anisotrophy J Biomol NMR. 2004 Oct;30(2):121-31 * Gronwald W, Moussa S, Elsner R, Jung A, Ganslmeier B, Trenner J, Kremer W, Neidig KP, Kalbitzer HR Automated assignment of NOESY NMR spectra using a knowledge based method (KNOWNOE) J Biomol NMR. 2002 Aug;23(4):271-87 * Gronwald W, Kirchhofer R, Gorler A, Kremer W, Ganslmeier B, Neidig KP, Kalbitzer HR RFAC, a program for automated NMR R-factor estimation J Biomol NMR. 2000 Jun;17(2):137-51 label = Label(frameB, text=text) label.grid(row=1, column=0, sticky='w') """ # # Frame C # # # Frame D # self.updateAll() def getEntryData(self): segLevel = self.segLevelEntry.get() or 0.001 threshold = self.thresholdEntry.get() or 100000 maxPeaks = self.keepPeakEntry.get() or 1 segLevel = min(1.0, segLevel) self.segLevelEntry.set(segLevel) self.thresholdEntry.set(threshold) self.keepPeakEntry.set(maxPeaks) return segLevel, threshold, maxPeaks def pickThreshold(self): if self.spectrum: segLevel, threshold, maxPeaks = self.getEntryData() try: findAuremolPeaksThreshold(spectrum=self.spectrum, mode=self.peakMode, useAutoThreshold=0, threshold=threshold, seglevel=segLevel) except Exception, e: showError('pickThreshold', str(e), parent=self)
class EditPeakFindParamsPopup(BasePopup): """ ** Peak Settings and Non-Interactive Peak Finding ** The purpose of this dialog is to allow the user to select settings for finding and integrating peaks, and also to be able to find peaks in an arbitrary region that is specified in a table rather than via a spectrum window. ** Find Parameters tab ** This can be used to specify how peak finding works. First of all, you can search for just positive peaks, just negative peaks or both, and the default is that it is just positive peaks. However, this is further filtered by what the contour levels are. If there are no positive contour levels for a given spectrum then positive peaks are not found even if this dialog says they can be, and similarly if there are no negative contour levels for a given spectrum then negative peaks are not found even if this dialog says they can be. The peak finding algorithm looks for local extrema (maximum for positive peaks and minima for negative peaks). But on a grid there are various ways to define what you mean by an extremum. Suppose you are trying to determine if point p is a maximum (similar considerations apply for minimum). You would want the intensity at all nearby points to be less than or equal to the intensity at p. You can just check points that are just +- one point from p in each dimension, or you can also check "diagonal" points. For example, if you are looking at point p = (x, y) in 2D, then the former would mean checking the four points (x-1, y), (x+1, y) (x, y-1) and (x, y+1), whereas for the latter you would also have to check (x-1, y-1), (x-1, y+1), (x+1, y-1) and (x+1, y+1). In N dimensions the "diagonal" method involves checking 3^N-1 points whereas the "non-diagonal" method involves checking only 2N points. In general the "non-diagonal" method is probably the one to use, and it is the default. Peaks are only found above (for positive peaks) or below (for negative peaks) some threshold. By default this is determined by the contour level for the spectrum. For positive peaks the threshold is the minimum positive contour level, and for negative peaks the threshold is the maximum negative contour level. However these levels can be scaled up (or down) using the "Scale relative to contour levels" option (default value 1). For example, if you have drawn the contour levels low to show a bit of noise, but do not want the noise picked as peaks, then you could select a scale of 2 (or whatever) to increase the threshold. The "Exclusion buffer around peaks" is so that in crowded regions you do not get too many peaks near one location. By default the exclusion buffer is 1 point in each dimension, but this can be increased to make the algorithm find fewer peaks. By default the peak finding only looks at the orthogonal region that is displayed in the given window where peak finding is taking place. Sometimes it looks like a peak should be found because in x, y you can see an extremum, but unless it is also an extremum in the orthogonal dimensions it is not picked. You can widen out the points being examined in the orthogonal dimensions by using the "Extra thickness in orthogonal dims" option, which is specified in points. The "Minimum drop factor" is by what factor the intensity needs to drop from its extreme value for there to be considered to be a peak. This could help remove sinc wiggle peaks, for example. The default is that the drop factor is 0, which in effect means that there is no condition. The "Volume method" is what is used to estimate the volume of peaks that are found. The default is "box sum", which just looks at a fixed size box around the peak centre and sums the intensities in that. The size of the box is set in the table in the Spectrum Widths tab. The "truncated box sum" is the same as "box sum" except that the summing stops in a given direction when (if) the intensities start increasing. The "parabolic" fit fits a quadratic equation in each dimension to the intensity at the peak centre and ad +- 1 points and then uses the equivalent Gaussian fit to estimate the volume. ** Spectrum Widths ** This can be used to specify minimum linewidths (in Hz) for there to be considered a peak to exist in the peak finding algorithm. It is also where the Boxwidth for each dimension in each spectrum is specified. ** Diagonal Exclusions ** This can be used to exclude peaks from being found in regions near the diagonal (so in homonuclear experiments). The exclusion region is specified in ppm and is independent of spectrum. ** Region Peak Find ** This can be used to find peaks non-interactively (so not having to control shift drag inside a spectrum window). The region being analysed is specified in the table. There are two types of conditions that can be specified, "include" for regions that should be included and "exclude" for regions that should be excluded. The regions are specified in ppm. The "Whole Region" button will set the selected row in the table to be the entire fundamental region of the spectrum. The "Add Region" button adds an extra row to the table, and the "Delete Region" button removes the selected row. The "Adjust Params" button goes to the Find Parameters tab. The "Find Peaks!" button does the peak finding. """ def __init__(self, parent, *args, **kw): self.spectrum = None BasePopup.__init__(self, parent=parent, title='Peak : Peak Finding', **kw) def body(self, guiFrame): self.geometry('600x350') guiFrame.expandGrid(0, 0) tipTexts = ['', '', '', ''] options = [ 'Find Parameters', 'Spectrum Widths', 'Diagonal Exclusions', 'Region Peak Find' ] tabbedFrame = TabbedFrame(guiFrame, options=options, grid=(0, 0)) frameA, frameB, frameC, frameD = tabbedFrame.frames self.tabbedFrame = tabbedFrame # Find Params frameA.expandGrid(2, 0) row = 0 label = LabelFrame(frameA, text='Extrema to search for:', grid=(row, 0), gridSpan=(1, 2)) label.expandGrid(0, 1) entries = ['positive and negative', 'positive only', 'negative only'] tipTexts = [ 'Sets whether peak picking within spectra find intensity maxima, minima or both maxima and minima', ] self.extrema_buttons = RadioButtons(label, entries=entries, select_callback=self.apply, direction='horizontal', grid=(0, 0), tipTexts=tipTexts) row += 1 label = LabelFrame(frameA, text='Nearby points to check:', grid=(row, 0), gridSpan=(1, 2)) label.expandGrid(None, 1) entries = ['+-1 in at most one dim', '+-1 allowed in any dim'] tipTexts = [ 'Sets how permissive the peak picking in when searching for intensity extrema; by adding extra points to the selected search region', ] self.adjacent_buttons = RadioButtons(label, entries=entries, select_callback=self.apply, direction='horizontal', grid=(0, 0), tipTexts=tipTexts) row += 1 labelFrame = LabelFrame(frameA, text='Other parameters:', grid=(row, 0), gridSpan=(1, 2)) labelFrame.expandGrid(5, 2) frow = 0 label = Label(labelFrame, text='Scale relative to contour levels:', grid=(frow, 0), sticky='e') tipText = 'Threshold above which peaks are picked, relative to the lowest displayed contour; 1.0 means picking exactly what is visible' self.scale_entry = FloatEntry(labelFrame, grid=(frow, 1), tipText=tipText, returnCallback=self.apply, width=10) self.scale_entry.bind('<Leave>', self.apply, '+') frow += 1 label = Label(labelFrame, text='Exclusion buffer around peaks (in points):', grid=(frow, 0), sticky='e') tipText = 'The size of the no-pick region, in data points, around existing picked peaks; eliminates duplicate picking' self.buffer_entry = IntEntry(labelFrame, returnCallback=self.apply, grid=(frow, 1), width=10, tipText=tipText) self.buffer_entry.bind('<Leave>', self.apply, '+') frow += 1 label = Label(labelFrame, text='Extra thickness in orthogonal dims (in points):', grid=(frow, 0), sticky='e') tipText = 'Sets whether to consider any additional planes (Z dimension) when calculating peak volume integrals' self.thickness_entry = IntEntry(labelFrame, returnCallback=self.apply, width=10, grid=(frow, 1), tipText=tipText) self.thickness_entry.bind('<Leave>', self.apply, '+') frow += 1 label = Label(labelFrame, text='Minimum drop factor (0.0-1.0):', grid=(frow, 0), sticky='e') tipText = '' self.drop_entry = FloatEntry(labelFrame, returnCallback=self.apply, width=10, grid=(frow, 1), tipText=tipText) self.drop_entry.bind('<Leave>', self.apply, '+') frow += 1 label = Label(labelFrame, text='Volume method:', grid=(frow, 0), sticky='e') tipText = 'Selects which method to use to calculate peak volume integrals when peaks are picked; box sizes are specified in "Spectrum Widths"' self.method_menu = PulldownList(labelFrame, texts=PeakBasic.PEAK_VOLUME_METHODS, grid=(frow, 1), callback=self.apply, tipText=tipText) # Spectrum widths frameB.expandGrid(1, 1) label = Label(frameB, text='Spectrum: ') label.grid(row=0, column=0, sticky='e') tipText = 'The spectrum which determines the widths being shown' self.expt_spectrum = PulldownList(frameB, tipText=tipText, callback=self.setSpectrumProperties) self.expt_spectrum.grid(row=0, column=1, sticky='w') self.editLinewidthEntry = FloatEntry(self, text='', returnCallback=self.setLinewidth, width=10) self.editBoxwidthEntry = FloatEntry(self, text='', returnCallback=self.setBoxwidth, width=10) tipTexts = [ 'The number of the spectrum dimension to which the settings apply', 'The nuclear isotope measures in the spectrum dimension', 'The smallest value for the linewidth of a peak for it to be picked', 'The size of the spectrum region to perform the volume integral over' ] headingList = [ 'Dimension', 'Isotope', 'Minimum Linewidth (Hz)', 'Boxwidth' ] editSetCallbacks = [None, None, self.setLinewidth, self.setBoxwidth] editGetCallbacks = [None, None, self.getLinewidth, self.getBoxwidth] editWidgets = [ None, None, self.editLinewidthEntry, self.editBoxwidthEntry ] self.spectrumMatrix = ScrolledMatrix(frameB, initialRows=6, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, headingList=headingList, callback=self.selectCell, tipTexts=tipTexts) self.spectrumMatrix.grid(row=1, column=0, columnspan=2, sticky='nsew') # Diagonal Exclusions frameC.expandGrid(0, 0) tipTexts = [ 'The isotope as measures on the axis of a spectrum window', 'The distance from the homonuclear diagonal line within which no peak picking can occur' ] self.exclusionEntry = FloatEntry(self, text='', returnCallback=self.setExclusion, width=10) headingList = ['Isotope', 'Diagonal Exclusion (ppm)'] editSetCallbacks = [None, self.setExclusion] editGetCallbacks = [None, self.getExclusion] editWidgets = [None, self.exclusionEntry] self.isotopeMatrix = ScrolledMatrix(frameC, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, headingList=headingList, grid=(0, 0), tipTexts=tipTexts) # Region peak find self.regionFindPeakList = None self.regionCondition = None self.regionConditions = [] self.regionCol = 1 row = 0 label = Label(frameD, text='Peak List: ', grid=(0, 0)) tipText = 'Selects which peak list to perform region-wide peak picking for' self.regionPeakListPulldown = PulldownList( frameD, callback=self.changeRegionPeakList, grid=(0, 1), tipText=tipText) row += 1 frameD.expandGrid(row, 1) self.regionEntry = FloatEntry(self, text='', returnCallback=self.setRegion, width=10) self.conditionMenu = PulldownList(self, texts=('include', 'exclude'), callback=self.setCondition) tipTexts = [ 'Whether to include or exclude the states region from region-wide peak picking', ] headingList = ['Condition'] editSetCallbacks = [None] editGetCallbacks = [None] editWidgets = [self.conditionMenu] self.regionFindMatrix = ScrolledMatrix( frameD, headingList=headingList, callback=self.selectRegionCell, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, grid=(row, 0), gridSpan=(1, 2)) row += 1 tipTexts = [ 'Sets the currently selected region row to cover the whole spectrum', 'Add a new region row, which may them be set for exclusion or inclusion when peak picking large areas', 'Remove the selected region specification', 'Go to the panel for setting the parameters that control how peaks extrema are picked', 'Using the stated regions and parameters, perform region-wide peak picking' ] texts = [ 'Whole Region', 'Add Region', 'Delete Region', 'Adjust Params', 'Find Peaks!' ] commands = [ self.wholeRegion, self.addCondition, self.deleteCondition, self.adjustParams, self.regionFindPeaks ] buttons = ButtonList(frameD, texts=texts, commands=commands, grid=(row, 0), gridSpan=(1, 2), tipTexts=tipTexts) buttons.buttons[4].config(bg='#B0FFB0') utilButtons = UtilityButtonList(tabbedFrame.sideFrame, grid=(0, 0), helpUrl=self.help_url, sticky='e') self.dataDim = None self.setParamsEntries() self.updateSpectrum() self.setIsotopeProperties() self.updateRegionPeakLists() self.administerNotifiers(self.registerNotify) def administerNotifiers(self, notifyFunc): # Many more needed here, esp on the AnalysisProject prams for func in ('__init__', 'delete', 'setName'): notifyFunc(self.updateRegionPeakLists, 'ccp.nmr.Nmr.DataSource', func) notifyFunc(self.updateRegionPeakLists, 'ccp.nmr.Nmr.Experiment', func) for func in ('__init__', 'delete'): notifyFunc(self.updateRegionPeakLists, 'ccp.nmr.Nmr.PeakList', func) for clazz in ('Experiment', 'DataSource'): for func in ('__init__', 'delete', 'setName'): notifyFunc(self.updateSpectrumTable, 'ccp.nmr.Nmr.%s' % clazz, func) for func in ('setPeakFindBoxWidth', 'setPeakFindMinLineWidth'): notifyFunc(self.updateSpectrumTable, 'ccpnmr.Analysis.AnalysisDataDim', func) def destroy(self): self.administerNotifiers(self.unregisterNotify) BasePopup.destroy(self) def updateSpectrum(self, spectrum=None): if not spectrum: spectrum = self.spectrum spectra = self.parent.getSpectra() if spectra: if spectrum not in spectra: spectrum = spectra[0] index = spectra.index(spectrum) names = ['%s:%s' % (x.experiment.name, x.name) for x in spectra] else: index = 0 names = [] self.expt_spectrum.setup(names, spectra, index) self.setSpectrumProperties(spectrum) def updateNotifier(self, *extra): self.updateSpectrum() def setLinewidth(self, *event): value = self.editLinewidthEntry.get() if value is not None: PeakFindParams.setPeakFindMinLinewidth(self.dataDim, value) self.setSpectrumProperties(self.dataDim.dataSource) def getLinewidth(self, dataDim): if dataDim: self.editLinewidthEntry.set( PeakFindParams.getPeakFindMinLinewidth(self.dataDim)) def setBoxwidth(self, *event): value = self.editBoxwidthEntry.get() if value is not None: PeakFindParams.setPeakFindBoxwidth(self.dataDim, value) self.setSpectrumProperties(self.dataDim.dataSource) def getBoxwidth(self, dataDim): if dataDim: self.editBoxwidthEntry.set( PeakFindParams.getPeakFindBoxwidth(self.dataDim)) def selectCell(self, object, row, col): self.dataDim = object def setExclusion(self, *extra): isotope = self.isotopeMatrix.currentObject if not isotope: return value = self.exclusionEntry.get() if value is not None: setIsotopeExclusion(isotope, value) self.setIsotopeProperties() def getExclusion(self, isotope): value = getIsotopeExclusion(isotope) self.exclusionEntry.set(value) def setParamsEntries(self): project = self.project params = PeakFindParams.getPeakFindParams(project) self.scale_entry.set(params['scale']) self.buffer_entry.set(params['buffer']) self.thickness_entry.set(params['thickness']) self.drop_entry.set(params['drop']) volumeMethod = params['volumeMethod'] if volumeMethod == 'parabolic fit': volumeMethod = PeakBasic.PEAK_VOLUME_METHODS[0] self.method_menu.set(params['volumeMethod']) if (params['nonadjacent']): n = 1 else: n = 0 self.adjacent_buttons.setIndex(n) have_high = params['haveHigh'] have_low = params['haveLow'] if (have_high and have_low): n = 0 elif (have_high): n = 1 else: n = 2 self.extrema_buttons.setIndex(n) def apply(self, *extra): params = {} params['scale'] = self.scale_entry.get() params['buffer'] = self.buffer_entry.get() params['thickness'] = self.thickness_entry.get() params['drop'] = self.drop_entry.get() params['volumeMethod'] = self.method_menu.getText() n = self.adjacent_buttons.getIndex() if (n == 0): nonadjacent = False else: nonadjacent = True params['nonadjacent'] = nonadjacent n = self.extrema_buttons.getIndex() if (n == 0): have_high = True have_low = True elif (n == 1): have_high = True have_low = False elif (n == 2): have_high = False have_low = True params['haveHigh'] = have_high params['haveLow'] = have_low project = self.project try: PeakFindParams.setPeakFindParams(project, params) except Implementation.ApiError, e: showError('Parameter error', e.error_msg, parent=self)
class MeccanoPopup(BasePopup): def __init__(self, parent, project, *args, **kw): self.alignMedium = None self.chain = None self.constraint = None self.constraintSet = None self.molSystem = None self.project = project self.run = None self.shiftList = None self.tensor = None BasePopup.__init__(self, parent=parent, title='MECCANO', *args, **kw) self.curateNotifiers(self.registerNotify) def body(self, guiFrame): guiFrame.grid_columnconfigure(0, weight=1) guiFrame.grid_rowconfigure(0, weight=1) options = ['Parameters','Restraints','Alignment Media & Tensors','About Meccano'] tabbedFrame = TabbedFrame(guiFrame, options=options) tabbedFrame.grid(row=0, column=0, sticky='nsew') frameA, frameB, frameC, frameD = tabbedFrame.frames frameA.grid_columnconfigure(1, weight=1) frameA.grid_rowconfigure(13, weight=1) frameB.grid_columnconfigure(1, weight=1) frameB.grid_rowconfigure(1, weight=1) frameC.grid_columnconfigure(0, weight=1) frameC.grid_rowconfigure(1, weight=1) frameD.grid_columnconfigure(0, weight=1) frameD.grid_rowconfigure(0, weight=1) texts = ['Run MECCANO!'] commands = [self.runMeccano] bottomButtons = createDismissHelpButtonList(guiFrame, texts=texts, commands=commands, expands=True) bottomButtons.grid(row=1, column=0, sticky='ew') if not Meccano: bottomButtons.buttons[0].disable() # Parameters row = 0 label = Label(frameA, text='Calculation Run:') label.grid(row=row,column=0,sticky='w') self.runPulldown = PulldownList(frameA, callback=self.selectRun) self.runPulldown.grid(row=row,column=1,sticky='w') row += 1 label = Label(frameA, text='Shift List (for CO):') label.grid(row=row,column=0,sticky='w') self.shiftListPulldown = PulldownList(frameA, callback=self.selectShiftList) self.shiftListPulldown.grid(row=row,column=1,sticky='w') row += 1 label = Label(frameA, text='Keep Copy of Used Shifts:') label.grid(row=row,column=0,sticky='w') self.toggleCopyShifts = CheckButton(frameA) self.toggleCopyShifts.grid(row=row,column=1,sticky='w') self.toggleCopyShifts.set(True) row += 1 label = Label(frameA, text='Molecular System:') label.grid(row=row,column=0,sticky='w') self.molSystemPulldown = PulldownList(frameA, callback=self.selectMolSystem) self.molSystemPulldown.grid(row=row,column=1,sticky='w') row += 1 label = Label(frameA, text='Chain:') label.grid(row=row,column=0,sticky='w') self.chainPulldown = PulldownList(frameA, callback=self.selectChain) self.chainPulldown.grid(row=row,column=1,sticky='w') self.chainPulldown.bind('<Leave>', self.updateRunParams) row += 1 label = Label(frameA, text='First Peptide Plane:') label.grid(row=row,column=0,sticky='w') self.firstResEntry = IntEntry(frameA, text=None, width=8) self.firstResEntry.grid(row=row,column=1,sticky='w') self.firstResEntry.bind('<Leave>', self.updateRunParams) row += 1 label = Label(frameA, text='Last Peptide Plane:') label.grid(row=row,column=0,sticky='w') self.lastResEntry = IntEntry(frameA, text=None, width=8) self.lastResEntry.grid(row=row,column=1,sticky='w') self.lastResEntry.bind('<Leave>', self.updateRunParams) row += 1 label = Label(frameA, text='Max Num Optimisation Steps:') label.grid(row=row,column=0,sticky='w') self.maxOptStepEntry = IntEntry(frameA, text=500, width=8) self.maxOptStepEntry.grid(row=row,column=1,sticky='w') self.maxOptStepEntry.bind('<Leave>', self.updateRunParams) row += 1 label = Label(frameA, text='Num Optimisation Peptide Planes:') label.grid(row=row,column=0,sticky='w') self.numOptPlaneEntry = IntEntry(frameA, text=2, width=8) self.numOptPlaneEntry.grid(row=row,column=1,sticky='w') self.numOptPlaneEntry.bind('<Leave>', self.updateRunParams) row += 1 label = Label(frameA, text='Min Num Optimisation Hits:') label.grid(row=row,column=0,sticky='w') self.numOptHitsEntry = IntEntry(frameA, text=5, width=8) self.numOptHitsEntry.grid(row=row,column=1,sticky='w') self.numOptHitsEntry.bind('<Leave>', self.updateRunParams) row += 1 label = Label(frameA, text='File Name Prefix:') label.grid(row=row,column=0,sticky='w') self.pdbFileEntry = Entry(frameA, text='Meccano', width=8) self.pdbFileEntry.grid(row=row,column=1,sticky='w') self.pdbFileEntry.bind('<Leave>', self.updateRunParams) row += 1 label = Label(frameA, text='Write Output File (.out):') label.grid(row=row,column=0,sticky='w') self.toggleWriteOutFile = CheckButton(frameA) self.toggleWriteOutFile.grid(row=row,column=1,sticky='w') self.toggleWriteOutFile.set(False) self.toggleWriteOutFile.bind('<Leave>', self.updateRunParams) row += 1 label = Label(frameA, text='Write PDB File (.pdb):') label.grid(row=row,column=0,sticky='w') self.toggleWritePdbFile = CheckButton(frameA) self.toggleWritePdbFile.grid(row=row,column=1,sticky='w') self.toggleWritePdbFile.set(True) self.toggleWritePdbFile.bind('<Leave>', self.updateRunParams) if not Meccano: row += 1 label = Label(frameA, text='The Meccano executable is not available (it needs to be compiled)', fg='red') label.grid(row=row,column=0,columnspan=2,sticky='w') # Restraints label = Label(frameB, text='Constraint Set:') label.grid(row=0,column=0,sticky='w') self.constraintSetPulldown = PulldownList(frameB, callback=self.selectConstraintSet) self.constraintSetPulldown.grid(row=0,column=1,sticky='w') self.alignMediumPulldown= PulldownList(self, callback=self.setAlignMedium) headingList = ['#','List Type','Use?','Alignment\nMedium','Num\nRestraints'] editWidgets = [None,None,None,self.alignMediumPulldown,None] editGetCallbacks = [None,None,self.toggleUseRestraints,self.getAlignMedium,None] editSetCallbacks = [None,None,None,self.setAlignMedium,None] self.restraintMatrix = ScrolledMatrix(frameB, headingList=headingList, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, callback=None, multiSelect=True) self.restraintMatrix.grid(row=1,column=0,columnspan=2,sticky='nsew') # Alignment Media div = LabelDivider(frameC,text='Alignment Media') div.grid(row=0,column=0,sticky='ew') self.mediumNameEntry = Entry(self, returnCallback=self.setMediumName) self.mediumDetailsEntry = Entry(self, returnCallback=self.setMediumDetails) headingList = ['#','Name','Details','Static Tensor','Dynamic Tensor'] editWidgets = [None, self.mediumNameEntry, self.mediumDetailsEntry, None, None] editGetCallbacks = [None, self.getMediumName, self.getMediumDetails, None, None] editSetCallbacks = [None, self.setMediumName, self.setMediumDetails, None, None] self.mediaMatrix = ScrolledMatrix(frameC, headingList=headingList, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, callback=self.selectAlignMedium, multiSelect=True) self.mediaMatrix.grid(row=1,column=0,sticky='nsew') texts = ['Add Alignment medium','Remove Alignment Medium'] commands = [self.addAlignMedium,self.removeAlignMedium] buttonList = ButtonList(frameC, texts=texts, commands=commands, expands=True) buttonList.grid(row=2,column=0,sticky='nsew') self.editAxialEntry = FloatEntry(self, returnCallback=self.setAxial) self.editRhombicEntry = FloatEntry(self, returnCallback=self.setRhombic) self.editAlphaEulerEntry = FloatEntry(self, returnCallback=self.setEulerAlpha) self.editBetaEulerEntry = FloatEntry(self, returnCallback=self.setEulerBeta) self.editGammaEulerEntry = FloatEntry(self, returnCallback=self.setEulerGamma) div = LabelDivider(frameC,text='Alignment Tensors') div.grid(row=3,column=0,sticky='ew') headingList = ['Type', u'Axial (\u03B6)',u'Rhombic (\u03B7)', u'Euler \u03B1',u'Euler \u03B2',u'Euler \u03B3'] editWidgets = [None,self.editAxialEntry, self.editRhombicEntry,self.editAlphaEulerEntry, self.editBetaEulerEntry,self.editGammaEulerEntry] editSetCallbacks = [None,self.setAxial,self.setRhombic, self.setEulerAlpha,self.setEulerBeta,self.setEulerGamma] editGetCallbacks = [None,self.getAxial,self.getRhombic, self.getEulerAlpha,self.getEulerBeta,self.getEulerGamma] self.tensorMatrix = ScrolledMatrix(frameC, maxRows=2, headingList=headingList, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, callback=self.selectTensor, multiSelect=True) self.tensorMatrix.grid(row=4,column=0,sticky='nsew') texts = ['Add Static Tensor','Add Dynamic Tensor','Remove Tensor'] commands = [self.addStaticTensor,self.addDynamicTensor,self.removeTensor] buttonList = ButtonList(frameC,texts=texts, commands=commands, expands=True) buttonList.grid(row=5,column=0,sticky='ew') # About label = Label(frameD, text='About Meccano...') label.grid(row=0,column=0,sticky='w') # self.geometry('500x400') self.updateShiftLists() self.updateMolSystems() self.updateResidueRanges() self.updateConstraintSets() self.updateAlignMedia() self.updateRuns() def close(self): self.updateRunParams() BasePopup.close(self) def destroy(self): self.updateRunParams() self.curateNotifiers(self.unregisterNotify) BasePopup.destroy(self) def curateNotifiers(self, notifyFunc): for func in ('__init__', 'delete'): notifyFunc(self.updateConstraintSetsAfter, 'ccp.nmr.NmrConstraint.NmrConstraintStore', func) for func in ('__init__', 'delete','setName','setConditionState'): for clazz in ('ccp.nmr.NmrConstraint.CsaConstraintList', 'ccp.nmr.NmrConstraint.DihedralConstraintList', 'ccp.nmr.NmrConstraint.DistanceConstraintList', 'ccp.nmr.NmrConstraint.HBondConstraintList', 'ccp.nmr.NmrConstraint.JCouplingConstraintList', 'ccp.nmr.NmrConstraint.RdcConstraintList'): notifyFunc(self.updateConstraintListsAfter, clazz, func) for func in ('__init__', 'delete',): for clazz in ('ccp.nmr.NmrConstraint.CsaConstraint', 'ccp.nmr.NmrConstraint.DihedralConstraint', 'ccp.nmr.NmrConstraint.DistanceConstraint', 'ccp.nmr.NmrConstraint.HBondConstraint', 'ccp.nmr.NmrConstraint.JCouplingConstraint', 'ccp.nmr.NmrConstraint.RdcConstraint'): notifyFunc(self.updateConstraintsAfter, clazz, func) for func in ('__init__', 'delete'): notifyFunc(self.updateShiftListsAfter,'ccp.nmr.Nmr.ShiftList', func) for func in ('__init__', 'delete'): notifyFunc(self.updateMolSystemsAfter,'ccp.molecule.MolSystem.MolSystem', func) for func in ('__init__', 'delete'): notifyFunc(self.updateChainsAfter,'ccp.molecule.MolSystem.Chain', func) for func in ('__init__', 'delete','setDynamicAlignment', 'setStaticAlignment','setName','setDetails'): notifyFunc(self.updateAlignMediaAfter,'ccp.nmr.NmrConstraint.ConditionState', func) def updateAlignMediaAfter(self, alignMedium): if alignMedium.nmrConstraintStore is self.constraintSet: self.after_idle(self.updateAlignMedia) if alignMedium is self.alignMedium: self.after_idle(self.updateTensors) def updateConstraintSetsAfter(self, constraintSet): self.after_idle(self.updateConstraintSets) def updateShiftListsAfter(self, shiftList): self.after_idle(self.updateShiftLists) def updateMolSystemsAfter(self, molSystem): self.after_idle(self.updateMolSystems) def updateChainsAfter(self, chain): self.after_idle(self.updateChains) def updateConstraintsAfter(self, constraint): if constraint.parent.parent is self.constraintSet: self.after_idle(self.updateConstraintLists) def updateConstraintListsAfter(self, constraintList): if constraintList.parent is self.constraintSet: self.after_idle(self.updateConstraintLists) def runMeccano(self): # # # Input checks first # # warning = '' if not self.molSystem: warning += 'No molecular system selected\n' if not self.chain: warning += 'No chain selected\n' if not self.constraintSet: warning += 'No selected constraint set\n' else: constraintLists = [cl for cl in self.constraintSet.constraintLists if cl.useForMeccano] if not constraintLists: warning += 'No constraint lists selected for use\n' first, last = self.updateResidueRanges() if (last-first) < 2: warning += 'Too few peptide planes selected\n' if warning: showWarning('Cannot run MECCANO','Encountered the following problems:\n' + warning) return if not self.run: self.run = self.makeSimRun() self.updateRunParams() if self.toggleCopyShifts.get() and self.shiftList: shiftList = self.run.findFirstOutputMeasurementList(className='ShiftList') if not shiftList: shiftList = self.project.currentNmrProject.newShiftList(name='Meccano Input') self.run.setOutputMeasurementLists([shiftList,]) shiftDict = {} for shift in shiftList.shifts: shiftDict[shift.resonance] = shift for shift in self.shiftList.shifts: resonance = shift.resonance resonanceSet = resonance.resonanceSet if resonanceSet: atom = resonanceSet.findFirstAtomSet().findFirstAtom() if (atom.name == 'C') and (atom.residue.molResidue.molType == 'protein'): shift2 = shiftDict.get(resonance) if shift2: shift2.value = shift.value shift2.error = shift.error else: shiftList.newShift(resonance=resonance, value=shift.value, error=shift.error) # # # Accumulate data from CCPN data model & GUI # # # Sequence residues = self.chain.sortedResidues() residueDict = {} seqData = [] for residue in residues: molResidue = residue.molResidue code1Letter = molResidue.chemComp.code1Letter if not code1Letter: msg = 'Encountered non-standard residue type: %s' showWarning('Cannot run MECCANO', msg % residue.ccpCode) return seqCode = residue.seqCode seqData.append((seqCode, code1Letter)) residueDict[seqCode] = residue.chemCompVar.chemComp.code3Letter # Media, RDCs & Dihedrals rdcLists = [] dihedralLists = [] for constraintList in constraintLists: if constraintList.className == 'RdcConsraintList': if constraintList.conditionState: rdcLists.append(constraintList) elif constraintList.className == 'DihedralConstraintList': dihedralLists.append(dihedralLists) f = PI_OVER_180 mediaData = [] for constraintList in rdcLists: medium = constraintList.conditionState dynamicTensor = medium.dynamicAlignment staticTensor = medium.staticAlignment if not (dynamicTensor or staticTensor): continue if dynamicTensor: dynamicTensorData = ['Dynamic', dynamicTensor.aAxial, dynamicTensor.aRhombic, f*dynamicTensor.alpha, f*dynamicTensor.beta, f*dynamicTensor.gamma] if staticTensor: staticTensorData = ['Static', staticTensor.aAxial, staticTensor.aRhombic, f*staticTensor.alpha, f*staticTensor.beta, f*staticTensor.gamma] if not dynamicTensor: dynamicTensorData = staticTensorData elif not staticTensor: staticTensorData = dynamicTensorData rdcData = [] for restraint in constraintList.constraints: items = list(restraint.items) if len(items) != 1: continue resonanceA, resonanceB = [fr.resonance for fr in items[0].resonances] resonanceSetA = resonanceA.resonanceSet if not resonanceSetA: continue resonanceSetB = resonanceB.resonanceSet if not resonanceSetB: continue resNameA = getResonanceName(resonanceA) resNameB = getResonanceName(resonanceB) residueA = resonanceSetA.findFirstAtomSet().findFirstAtom().residue residueB = resonanceSetB.findFirstAtomSet().findFirstAtom().residue seqA = residueA.seqCode seqB = residueB.seqCode value = restraint.targetValue error = restraint.error if error is None: key = [resNameA,resNameB] key.sort() sigma = DEFAULT_ERRORS.get(tuple(key), 1.0) rdcData.append([seqA, resNameA, seqB, resNameB, value, error]) mediaData.append((dynamicTensorData,staticTensorData,rdcData)) oneTurn = 360.0 dihedralDict = {} for constraintList in dihedralLists: for restraint in constraintList.constraints: items = list(restraint.items) if len(items) != 1: continue item = items[0] resonances = [fr.resonance for fr in item.resonances] resonanceSets = [r.resonanceSet for r in resonances] if None in resonanceSets: continue atoms = [rs.findFirstAtomSet().findFirstAtom() for rs in resonanceSets] atomNames = [a.name for a in atoms] if atomNames == PHI_ATOM_NAMES: isPhi = True elif atomNames == PSI_ATOM_NAMES: isPhi = False else: continue residue = atoms[2].residue if residue.chain is not self.chain: continue if isPhi: residuePrev = getLinkedResidue(residue, linkCode='prev') if not residuePrev: continue atomC0 = residuePrev.findFirstAtom(name='C') atomN = residue.findFirstAtom(name='N') atomCa = residue.findFirstAtom(name='CA') atomC = residue.findFirstAtom(name='C') atoms2 = [atomC0, atomN, atomCa, atomC] else: residueNext = getLinkedResidue(residue, linkCode='next') if not residueNext: continue atomN = residue.findFirstAtom(name='N') atomCa = residue.findFirstAtom(name='CA') atomC = residue.findFirstAtom(name='C') atomN2 = residueNext.findFirstAtom(name='N') atoms2 = [atomN, atomCa, atomC, atomN2] if atoms != atoms2: continue value = item.targetValue error = item.error if error is None: upper = item.upperLimit lower = item.lowerLimit if (upper is not None) and (lower is not None): dUpper = angleDifference(value, lower, oneTurn) dLower = angleDifference(upper, value, oneTurn) error = max(dUpper, dLower) elif lower is not None: error = angleDifference(value, lower, oneTurn) elif upper is not None: error = angleDifference(upper, value, oneTurn) else: error = 30.0 # Arbitrary, but sensible for TALOS, DANGLE seqCode = residue.seqCode if not dihedralDict.has_key(seqCode): dihedralDict[seqCode] = [None, None, None, None] # Phi, Psi, PhiErr, PsiErr if isPhi: dihedralDict[seqCode][0] = value dihedralDict[seqCode][2] = error else: dihedralDict[seqCode][1] = value dihedralDict[seqCode][3] = error phipsiData = [] seqCodes = dihedralDict.keys() seqCodes.sort() for seqCode in seqCodes: data = dihedralDict[seqCode] if None not in data: phi, psi, phiErr, psiErr = data phipsiData.append((seqCode, phi, psi, phiErr, psiErr)) # User options firstPPlaneFrag = self.firstResEntry.get() or 1 lastPPlaneFrag = self.lastResEntry.get() or 1 ppNbMin = self.numOptPlaneEntry.get() or 1 minValueBest = self.numOptHitsEntry.get() or 2 maxValueBest = self.maxOptStepEntry.get() or 5 strucData = Meccano.runFwd(firstPPlaneFrag, lastPPlaneFrag, ppNbMin, minValueBest, maxValueBest, RAMACHANDRAN_DATABASE, seqData, mediaData, phipsiData) if strucData: fileName = 'CcpnMeccanoPdb%f.pdb' % time.time() fileObj = open(fileName, 'w') ch = self.chain.pdbOneLetterCode.strip() if not ch: ch = self.chain.code[0].upper() i = 1 for atomType, resNb, x, y, z in strucData: resType = residueDict.get(resNb, '???') line = PDB_FORMAT % ('ATOM',i,'%-3s' % atomType,'',resType,ch,resNb,'',x,y,z,1.0,1.0) i += 1 fileObj.close() ensemble = getStructureFromFile(self.molSystem, fileName) if not self.toggleWritePdbFile.get(): os.unlink(fileName) self.run.outputEnsemble = ensemble self.run = None self.updateRuns() def getMediumName(self, alignMedium): self.mediumNameEntry.set(alignMedium.name) def getMediumDetails(self, alignMedium): self.mediumDetailsEntry.set(alignMedium.details) def setMediumName(self, event): value = self.mediumNameEntry.get() self.alignMedium.name = value or None def setMediumDetails(self, event): value = self.mediumDetailsEntry.get() self.alignMedium.details = value or None def setAlignMedium(self, alignMedium): if self.constraintSet: self.constraintSet.conditionState = alignMedium def getAlignMedium(self, constraintList): media = self.getAlignmentMedia() names = [am.name for am in media] if constraintList.conditionState in media: index = media.index(constraintList.conditionState) else: index = 0 self.alignMediumPulldown.setup(names, media, index) def toggleUseRestraints(self, constraintList): bool = constraintList.useForMeccano bool = not bool if bool and (not constraintList.conditionState) \ and (constraintList.className == 'RdcConsraintList'): msg = 'Cannot use RDC restraint list for Meccano ' msg += 'unless it is first associated with an amigment medium' showWarning('Warning', msg, parent=self) else: constraintList.useForMeccano = bool self.updateConstraintLists() def addStaticTensor(self): if self.alignMedium: tensor = Implementation.SymmTracelessMatrix(aAxial=0.0,aRhombic=0.0, alpha=0.0,beta=0.0, gamma=0.0) self.alignMedium.staticAlignment = tensor self.updateAlignMediaAfter(self.alignMedium) def addDynamicTensor(self): if self.alignMedium: tensor = Implementation.SymmTracelessMatrix(aAxial=0.0,aRhombic=0.0, alpha=0.0,beta=0.0, gamma=0.0) self.alignMedium.dynamicAlignment = tensor self.updateAlignMediaAfter(self.alignMedium) def removeTensor(self): if self.alignMedium and self.tensor: if self.tensor is self.alignMedium.dynamicAlignment: self.alignMedium.dynamicAlignment = None elif self.tensor is self.alignMedium.staticAlignment: self.alignMedium.staticAlignment = None self.updateAlignMediaAfter(self.alignMedium) def addAlignMedium(self): if self.constraintSet: medium = self.constraintSet.newConditionState() medium.name = 'Align Medium %d' % medium.serial def removeAlignMedium(self): if self.alignMedium: self.alignMedium.delete() def updateTensor(self, aAxial=None, aRhombic=None, alpha=None, beta=None, gamma=None): aAxial = aAxial or self.tensor.aAxial aRhombic = aRhombic or self.tensor.aRhombic alpha = alpha or self.tensor.alpha beta = beta or self.tensor.beta gamma = gamma or self.tensor.gamma tensor = Implementation.SymmTracelessMatrix(aAxial=aAxial, aRhombic=aRhombic, alpha=alpha,beta=beta, gamma=gamma) if self.alignMedium: if self.tensor is self.alignMedium.dynamicAlignment: self.alignMedium.dynamicAlignment = tensor elif self.tensor is self.alignMedium.staticAlignment: self.alignMedium.staticAlignment = tensor self.tensor = tensor def setAxial(self, event): value = self.editAxialEntry.get() self.updateTensor(aAxial=value) self.updateTensors() def setRhombic(self, event): value = self.editRhombicEntry.get() self.updateTensor(aRhombic=value) self.updateTensors() def setEulerAlpha(self, event): value = self.editAlphaEulerEntry.get() self.updateTensor(alpha=value) self.updateTensors() def setEulerBeta(self, event): value = self.editBetaEulerEntry.get() self.updateTensor(beta=value) self.updateTensors() def setEulerGamma(self, event): value = self.editGammaEulerEntry.get() self.updateTensor(gamma=value) self.updateTensors() def getAxial(self, tensor): value = tensor.aAxial self.editAxialEntry.set(value) def getRhombic(self, tensor): value = tensor.aRhombic self.editRhombicEntry.set(value) def getEulerAlpha(self, tensor): value = tensor.alpha self.editAlphaEulerEntry.set(value) def getEulerBeta(self, tensor): value = tensor.beta self.editBetaEulerEntry.set(value) def getEulerGamma(self, tensor): value = tensor.gamma self.editGammaEulerEntry.set(value) def selectTensor(self, tensor, row, col): self.tensor = tensor def selectAlignMedium(self, alignMedium, row, col): self.alignMedium = alignMedium self.updateTensors() def getAlignmentMedia(self): if self.constraintSet: return self.constraintSet.sortedConditionStates() else: return [] def updateAlignMedia(self): textMatrix = [] objectList = [] if self.constraintSet: objectList = self.getAlignmentMedia() for conditionState in objectList: staticTensor = None dyamicTensor = None tensor = conditionState.dynamicAlignment if tensor: vals = (tensor.aAxial, tensor.aRhombic, tensor.alpha, tensor.beta, tensor.gamma) dyamicTensor = u'\u03B6:%.3f \u03B7:%.3f \u03B1:%.3f \u03B2:%.3f \u03B3:%.3f ' % vals tensor = conditionState.staticAlignment if tensor: vals = (tensor.aAxial, tensor.aRhombic, tensor.alpha, tensor.beta, tensor.gamma) staticTensor = u'\u03B6:%.3f \u03B7:%.3f \u03B1:%.3f \u03B2:%.3f \u03B3:%.3f ' % vals datum = [conditionState.serial, conditionState.name, conditionState.details, dyamicTensor, staticTensor] textMatrix.append(datum) if dyamicTensor or staticTensor: if not self.alignMedium: self.alignMedium = conditionState self.mediaMatrix.update(textMatrix=textMatrix, objectList=objectList) if self.alignMedium: self.mediaMatrix.selectObject(self.alignMedium) def updateTensors(self): textMatrix = [] objectList = [] conditionState = self.alignMedium if conditionState: tensor = conditionState.dynamicAlignment if tensor: datum = ['Dynamic', tensor.aAxial, tensor.aRhombic, tensor.alpha, tensor.beta, tensor.gamma] textMatrix.append(datum) objectList.append(tensor) tensor = conditionState.staticAlignment if tensor: datum = ['Static', tensor.aAxial, tensor.aRhombic, tensor.alpha, tensor.beta, tensor.gamma] textMatrix.append(datum) objectList.append(tensor) self.tensorMatrix.update(textMatrix=textMatrix, objectList=objectList) def getMolSystems(self): molSystems = [] for molSystem in self.project.sortedMolSystems(): if molSystem.chains: molSystems.append(molSystem) return molSystems def updateMolSystems(self, *notifyObj): index = 0 names = [] molSystems = self.getMolSystems() molSystem = self.molSystem if molSystems: if molSystem not in molSystems: molSystem = molSystems[0] index = molSystems.index(molSystem) names = [ms.code for ms in molSystems] else: self.molSystem = None if self.molSystem is not molSystem: if self.run: self.run.molSystem = molSystem self.molSystem = molSystem self.updateChains() self.molSystemPulldown.setup(texts=names, objects=molSystems, index=index) def selectMolSystem(self, molSystem): if self.molSystem is not molSystem: if self.run: self.run.molSystem = molSystem self.molSystem = molSystem self.updateChains() def updateChains(self, *notifyObj): index = 0 names = [] chains = [] chain = self.chain if self.molSystem: chains = [c for c in self.molSystem.sortedChains() if c.residues] if chains: if chain not in chains: chain = chains[0] index = chains.index(chain) names = [c.code for c in chains] if chain is not self.chain: self.chain = chain self.updateResidueRanges() self.chainPulldown.setup(texts=names, objects=chains, index=index) def selectChain(self, chain): if chain is not self.chain: self.chain = chain self.updateResidueRanges() def updateResidueRanges(self): first = self.firstResEntry.get() last = self.lastResEntry.get() if self.chain: residues = self.chain.sortedResidues() firstSeq = residues[0].seqCode lastSeq = residues[-2].seqCode if first < firstSeq: first = firstSeq if last == first: last = lastSeq elif last > lastSeq: last = lastSeq if first > last: last, first = first, last self.firstResEntry.set(first) self.lastResEntry.set(last) return first, last def getConstraintSets(self): constraintSets = [] nmrProject = self.project.currentNmrProject for constraintSet in nmrProject.sortedNmrConstraintStores(): for constraintList in constraintSet.constraintLists: if constraintList.className not in ('ChemShiftConstraintList','somethingElse'): constraintSets.append(constraintSet) break return constraintSets def updateConstraintSets(self, *notifyObj): index = 0 names = [] constraintSets = self.getConstraintSets() constraintSet = self.constraintSet if constraintSets: if constraintSet not in constraintSets: constraintSet = constraintSets[0] index = constraintSets.index(constraintSet) names = ['%d' % cs.serial for cs in constraintSets] if constraintSet is not self.constraintSet: if self.run: self.run.inputConstraintStore = constraintSet self.constraintSet = constraintSet self.updateConstraintLists() self.constraintSetPulldown.setup(texts=names, objects=constraintSets, index=index) def selectConstraintSet(self, constraintSet): if self.constraintSet is not constraintSet: if self.run: self.run.inputConstraintStore = constraintSet self.constraintSet = constraintSet self.updateConstraintLists() def getConstraintLists(self): constraintLists = [] if self.constraintSet: for constraintList in self.constraintSet.sortedConstraintLists(): if constraintList.className not in ('ChemShiftConstraintList','somethingElse'): constraintLists.append(constraintList) return constraintLists def updateConstraintLists(self, *notifyObj): textMatrix = [] objectList = self.getConstraintLists() for constraintList in objectList: if not hasattr(constraintList, 'useForMeccano'): if constraintList.conditionState \ or (constraintList.className != 'RdcConstraintList'): bool = True else: bool = False constraintList.useForMeccano = bool if constraintList.conditionState: alignMedium = constraintList.conditionState.name else: alignMedium = None datum = [constraintList.serial, constraintList.className[:-14], constraintList.useForMeccano and 'Yes' or 'No', alignMedium, len(constraintList.constraints)] textMatrix.append(datum) self.restraintMatrix.update(textMatrix=textMatrix, objectList=objectList) def selectConstraint(self, obj, row, column): if self.constraint is not obj: self.constraint = obj def getSimStore(self): simStore = self.project.findFirstNmrSimStore(name='meccano') if not simStore: simStore = self.project.newNmrSimStore(name='meccano') return simStore def getRuns(self): runs = [None, ] if self.molSystem and self.constraintSet: simStore = self.getSimStore() runs += simStore.sortedRuns() return runs def updateRuns(self, *notifyObj): index = 0 names = ['<New>'] runs = self.getRuns() run = self.run if runs: if run not in runs: run = runs[0] index = runs.index(run) names += [r.serial for r in runs if r] if run is not self.run: self.updateConstraintSets() self.updateMolSystems() self.updateShiftLists() self.runPulldown.setup(names, runs, index) def updateRunParams(self, event=None): if self.run and self.molSystem and self.constraintSet: simRun = self.run simRun.inputConstraintStore = self.constraintSet simRun.molSystem = self.molSystem if self.shiftList: simRun.setInputMeasurementLists([self.shiftList,]) simRun.newRunParameter(code='FirstPepPlane',id=1, intValue=self.firstResEntry.get() or 0) simRun.newRunParameter(code='LastPepPlane' ,id=1, intValue=self.lastResEntry.get() or 0) simRun.newRunParameter(code='MaxOptSteps', id=1, intValue=self.maxOptStepEntry.get() or 0) simRun.newRunParameter(code='NumOptPlanes', id=1, intValue=self.numOptPlaneEntry.get() or 0) simRun.newRunParameter(code='MinOptHits', id=1, intValue=self.numOptHitsEntry.get() or 0) def makeSimRun(self, template=None): simStore = self.getSimStore() if template: molSystem = template.molSystem constraintSet = template.inputConstraintStore shiftList = template.findFirstInputMeasurementList(className='ShiftList') protocol = template.annealProtocol else: molSystem = self.molSystem constraintSet = self.constraintSet shiftList = self.shiftList protocol = self.annealProtocol simRun = simStore.newRun(annealProtocol=protocol, molSystem=molSystem, inputConstraintStore=protocol) if shiftList: simRun.addInputMeasurementList(shiftList) if template: for param in template.runParameters: simRun.newRunParameter(code=param.code, id=param.id, booleanValue=param.booleanValue, floatValue=param.floatValue, intValue=param.intValue, textValue=param.textValue) else: if self.chain: chainCode = self.chain.code else: chaincode = 'A' simRun.newRunParameter(code='FirstPepPlane',id=1, intValue=self.firstResEntry.get() or 0) simRun.newRunParameter(code='LastPepPlane' ,id=1, intValue=self.lastResEntry.get() or 0) simRun.newRunParameter(code='MaxOptSteps', id=1, intValue=self.maxOptStepEntry.get() or 0) simRun.newRunParameter(code='NumOptPlanes', id=1, intValue=self.numOptPlaneEntry.get() or 0) simRun.newRunParameter(code='MinOptHits', id=1, intValue=self.numOptHitsEntry.get() or 0) simRun.newRunParameter(code='ChainCode', id=1, textValue=chainCode) return simRun def selectRun(self, simRun): if self.run is not simRun: if simRun: if simRun.outputEnsemble: msg = 'Selected run has already been used to generate a structure.' msg += 'A new run will be setup based on the selection.' showWarning('Warning',msg) simRun = self.makeSimRun(template=simRun) molSystem = simRun.molSystem constraintSet = simRun.inputConstraintStore shiftList = simRun.findFirstInputMeasurementList(className='ShiftList') if molSystem and (self.molSystem is not molSystem): self.molSystem = molSystem self.updateMolSystems() if constraintSet and (self.constraintSet is not constraintSet): self.constraintSet = constraintSet self.updateConstraintSets() if shiftList and (self.shiftList is not shiftList): self.shiftList = shiftList self.updateShiftLists() firstPepPlane = simRun.findFirstrunParameter(code='FirstPepPlane') lastPepPlane = simRun.findFirstrunParameter(code='LastPepPlane') maxOptSteps = simRun.findFirstrunParameter(code='MaxOptSteps') numOptPlanes = simRun.findFirstrunParameter(code='NumOptPlanes') minOptHits = simRun.findFirstrunParameter(code='MinOptHits') chainCode = simRun.findFirstrunParameter(code='ChainCode') if firstPepPlane is not None: self.firstResEntry.set(firstPepPlane.intValue or 0) if lastPepPlane is not None: self.lastResEntry.set(lastPepPlane.intValue or 0) if maxOptSteps is not None: self.maxOptStepEntry.set(maxOptSteps.intValue or 0) if numOptPlanes is not None: self.numOptPlaneEntry.set(numOptPlanes.intValue or 0) if minOptHits is not None: self.numOptHitsEntry.set(minOptHits.intValue or 0) if chainCode is not None: chainCode = chainCode.textValue or 'A' if self.molSystem: chain = self.molSystem.findFirsChain(code=chainCode) if chain: self.selectChain(chain) self.run = simRun def updateShiftLists(self, *notifyObj): index = 0 names = [] nmrProject = self.project.currentNmrProject shiftLists = nmrProject.findAllMeasurementLists(className='ShiftList') shiftLists = [(sl.serial, sl) for sl in shiftLists] shiftLists.sort() shiftLists = [x[1] for x in shiftLists] shiftList = self.shiftList if shiftLists: if shiftList not in shiftLists: shiftList = shiftLists[0] index = shiftLists.index(shiftList) names = ['%d'% sl.serial for sl in shiftLists] if shiftList is not self.shiftList: if self.run: self.run.setInputMeasurementLists([shiftList]) self.shiftListPulldown.setup(texts=names, objects=shiftLists, index=index) def selectShiftList(self, shiftList): if shiftList is not self.shiftList: if self.run: self.run.setInputMeasurementLists([shiftList]) self.shiftList = shiftList
class EditNoeClassesPopup(BasePopup): def __init__(self, parent, *args, **kw): self.guiParent = parent BasePopup.__init__(self, parent=parent, title='NOE Distance Classes', **kw) def body(self, guiFrame): self.noeClassChoice = None self.spectrum = None self.intensEntry = FloatEntry(self, returnCallback=self.setIntens, width=5) self.targetEntry = FloatEntry(self, returnCallback=self.setTarget, width=5) self.minEntry = FloatEntry(self, returnCallback=self.setMin, width=5) self.maxEntry = FloatEntry(self, returnCallback=self.setMax, width=5) row = 0 label = Label(guiFrame, text='Spectrum: ', grid=(row,0)) tipText = '' self.spectrumPulldown = PulldownMenu(guiFrame,self.changeSpectrum, grid=(row,1)) row +=1 guiFrame.expandGrid(row, 1) tipTexts = ['Lower bound of this intensity category. Values are relative to reference intensity.', 'Target restraint distance for this category', 'Lower bound distance for this category', 'Upper bound distance for this category'] headingList = ['Min. NOE\nIntensity','Target\nDist','Min\nDist','Max\nDist'] editWidgets = [self.intensEntry,self.targetEntry,self.minEntry,self.maxEntry] editGetCallbacks = [self.getIntens,self.getTarget,self.getMin,self.getMax] editSetCallbacks = [self.setIntens,self.setTarget,self.setMin,self.setMax] self.noeClassMatrix = ScrolledMatrix(guiFrame, headingList=headingList, callback=self.selectClass, tipTexts=tipTexts, editWidgets=editWidgets, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, deleteFunc=self.deleteClass, grid=(row,0), gridSpan=(1,2)) row +=1 tipTexts = ['Add a new distance restraint category', 'Deleted selected restraint categor'] texts = ['Add Class','Delete Class'] commands = [self.addClass,self.deleteClass] self.bottomButtons = UtilityButtonList(guiFrame, doClone=False, grid=(row,0), gridSpan=(1,2), tipTexts=tipTexts, commands=commands, texts=texts) for func in ('__init__','delete','setName'): self.registerNotify(self.updateSpectra, 'ccp.nmr.Nmr.Experiment', func) self.registerNotify(self.updateSpectra, 'ccp.nmr.Nmr.DataSource', func) self.updateSpectra() self.update() def open(self): self.updateSpectra() self.update() BasePopup.open(self) def updateSpectra(self, *opt): spectra = self.getSpectra() if not spectra: return names = [self.getSpectrumName(x) for x in spectra] if (not self.spectrum) or (self.spectrum not in spectra): self.spectrum = spectra[0] self.spectrumPulldown.setup(names, names.index(self.getSpectrumName(self.spectrum)) ) self.update() def changeSpectrum(self, i, name): self.spectrum = self.getSpectra()[i] self.update() def getSpectrumName(self,spectrum): name = '%s:%s' % (spectrum.experiment.name,spectrum.name) return name def getSpectra(self): spectra = set() peakLists = getThroughSpacePeakLists(self.nmrProject) for peakList in peakLists: spectra.add(peakList.dataSource) spectra = list(spectra) spectra.sort() return spectra def selectClass(self, noeClass, row, col): if noeClass: self.noeClassChoice = (row, noeClass) if len(self.noeClassMatrix.objectList) > 1: self.bottomButtons.buttons[1].enable() else: self.bottomButtons.buttons[1].disable() def addClass(self): if self.spectrum: noeClass = [0.0,6.0,0.0,6.0] noeClasses = getIntensityDistanceTable(self.spectrum) noeClasses.append(noeClass) setSpectrumNoeDistanceClasses(self.spectrum, noeClasses) self.update() def deleteClass(self, *event): if self.spectrum: noeClasses = getIntensityDistanceTable(self.spectrum) if self.noeClassChoice and (self.noeClassChoice[1] in noeClasses): if len(noeClasses) > 1: (i,noeClass) = self.noeClassChoice noeClasses.remove(noeClass) self.noeClassChoice = None setSpectrumNoeDistanceClasses(self.spectrum, noeClasses) self.update() def setIntens(self, event): if self.noeClassChoice: val = self.intensEntry.get() or 0.0 self.noeClassChoice[1][0] = val self.updateClass() def getIntens(self, row): if row: self.intensEntry.set(row[0]) def setTarget(self, event): if self.noeClassChoice: val = self.targetEntry.get() or 0.0 self.noeClassChoice[1][1] = val self.updateClass() def getTarget(self, row): if row: self.targetEntry.set(row[1]) def setMin(self, event): if self.noeClassChoice: val = self.minEntry.get() or 0.0 self.noeClassChoice[1][2] = val self.updateClass() def getMin(self, row): if row: self.minEntry.set(row[2]) def setMax(self, event): if self.noeClassChoice: val = self.maxEntry.get() or 0.0 self.noeClassChoice[1][3] = val self.updateClass() def getMax(self, row): if row: self.maxEntry.set(row[3]) def getClasses(self): noeClasses = [] if self.spectrum: noeClasses = getIntensityDistanceTable(self.spectrum) if noeClasses: for i in range(len(noeClasses)): (intens,target,minimum,maximum) = noeClasses[i] if minimum > maximum: (minimum,maximum) = (maximum,minimum) minimum = min(target, minimum) maximum = max(target, maximum) intens = max(intens, 0.0) noeClasses[i] = [intens,target,minimum,maximum] noeClasses.sort() noeClasses.reverse() else: noeClasses = [] if self.spectrum: # default noeClasses = getIntensityDistanceTable(self.spectrum) return noeClasses def updateClass(self): if self.spectrum and self.noeClassChoice: (i, noeClass) = self.noeClassChoice noeClasses = getIntensityDistanceTable(self.spectrum) noeClasses[i] = noeClass setSpectrumNoeDistanceClasses(self.spectrum, noeClasses) self.update() def update(self): textMatrix = [] objectList = self.getClasses() if self.spectrum: if self.noeClassChoice and (len(objectList) > 1): self.bottomButtons.buttons[1].enable() else: self.bottomButtons.buttons[1].disable() self.bottomButtons.buttons[0].enable() else: self.bottomButtons.buttons[0].disable() self.bottomButtons.buttons[1].disable() for (intens,target,minimum,maximum) in objectList: datum = [] datum.append(intens) datum.append(target) datum.append(minimum) datum.append(maximum) textMatrix.append(datum) self.noeClassMatrix.update(objectList=objectList,textMatrix=textMatrix) if self.spectrum: setSpectrumNoeDistanceClasses(self.spectrum,objectList) def destroy(self): for func in ('__init__','delete','setName'): self.unregisterNotify(self.updateSpectra, 'ccp.nmr.Nmr.Experiment', func) self.unregisterNotify(self.updateSpectra, 'ccp.nmr.Nmr.DataSource', func) BasePopup.destroy(self)
class RegionSelector(Frame): def __init__(self, parent, label='', world_region=None, view_region=None, orient=Tkinter.HORIZONTAL, allow_resize=True, width=20, callback=None, borderwidth=1, show_text=True, text_color='#000000', text_decimals=2, units_scroll=0.1, pages_scroll=1.0, menu_entries=None, menu_callback=None, min_thickness=None, *args, **kw): self.menu_entries = menu_entries self.myCallback = callback Frame.__init__(self, parent, *args, **kw) self.text_decimals = text_decimals self.label = Label(self, text=label, width=4) self.menu = PulldownMenu(self, callback=menu_callback, entries=menu_entries) self.entry = FloatEntry(self, width=6, returnCallback=self.adjustScrollbar) self.region_scrollbar = RegionScrollbar(self, world_region=world_region, view_region=view_region, orient=orient, allow_resize=allow_resize, width=width, callback=self.doCallback, borderwidth=borderwidth, show_text=show_text, text_color=text_color, text_decimals=text_decimals, units_scroll=units_scroll, pages_scroll=pages_scroll, min_thickness=min_thickness) self.gridAll() def gridAll(self): col = 0 if (self.menu_entries and len(self.menu_entries) > 1): self.menu.grid(row=0, column=col, sticky=Tkinter.EW) col = col + 1 else: self.menu.grid_forget() self.label.grid(row=0, column=col, sticky=Tkinter.EW) col = col + 1 self.entry.grid(row=0, column=col, sticky=Tkinter.EW) self.grid_columnconfigure(col, weight=0) col = col + 1 self.region_scrollbar.grid(row=0, column=col, sticky=Tkinter.NSEW) self.grid_columnconfigure(col, weight=1) col = col + 1 self.grid_columnconfigure(col, weight=0) def setMinThickness(self, min_thickness): self.region_scrollbar.setMinThickness(min_thickness) def setMenuEntries(self, menu_entries): self.menu_entries = menu_entries self.menu.replace(menu_entries) self.gridAll() def getMenuEntry(self): return self.menu.getSelected() def adjustScrollbar(self, *event): try: x = float(self.entry.get()) except: showError('Entry error', 'Need to enter float in scrollbar box') self.setEntry() return (v0, v1) = self.region_scrollbar.view_region d = 0.5 * (v1 - v0) self.region_scrollbar.setViewRegion(x - d, x + d, do_callback=True) def doCallback(self, view_region): if (self.myCallback): self.myCallback(view_region) #print 'doCallback', view_region self.setEntry(view_region) def setEntry(self, view_region=None): if (not view_region): view_region = self.region_scrollbar.view_region (v0, v1) = view_region x = 0.5 * (v0 + v1) s = formatDecimals(x, decimals=self.text_decimals) self.entry.set(s) def __getattr__(self, name): # dispatch everything not defined by RegionSelector to scrollbar widget try: return getattr(self.__dict__['region_scrollbar'], name) except: raise AttributeError, "RegionSelector instance has no attribute '%s'" % name
class EditPeakPopup(BasePopup): """ **Edit Position, Intensity & Details for a Peak** This popup window provides an means of editing peak information as an alternative to editing values in the main peak tables. This popup is also used to specify parameters for when a new peak is explicitly added to a peak list using a tabular display. The user can specify the position of the peak's dimensions in ppm, Hz or data point units. Also, the user can adjust the height and volume peak intensity values and a textual "Details" field, ,which can carry the user's comments about the peak. When editing an existing peak, no changes are made to the peak until the [Update] button is pressed. Likewise for a new peak the [Add Peak] button commits the changes. If the popup window is closed before the changes are committed then the entire editing or peak addition operation is cancelled. """ def __init__(self, parent, peak=None, peakList=None, *args, **kw): self.titleColor = '#000080' self.numDims = 0 self.peak = peak kw['borderwidth'] = 6 BasePopup.__init__(self, parent=parent, title='Edit Peak', **kw) self.registerNotify(self.deletedPeak, 'ccp.nmr.Nmr.Peak', 'delete') for func in ('setAnnotation', 'setDetails', 'setFigOfMerit'): self.registerNotify(self.updatePeak, 'ccp.nmr.Nmr.Peak', func) for func in ('setAnnotation', 'setPosition', 'setNumAliasing'): self.registerNotify(self.updatePeak, 'ccp.nmr.Nmr.PeakDim', func) for func in ('__init__', 'delete', 'setValue'): self.registerNotify(self.updatePeak, 'ccp.nmr.Nmr.PeakIntensity', func) self.dimensionLabels = [] self.dimensionEntries = [] self.update(self.peak, peakList) def body(self, guiParent): self.geometry("+150+150") guiParent.grid_columnconfigure(0, weight=1) self.master_frame = guiParent units = ('ppm', 'point', 'Hz') self.unit = 'ppm' self.specLabel = Label(guiParent, fg=self.titleColor, grid=(0, 0), sticky='ew') self.peakLabel = Label(guiParent, grid=(0, 1), sticky='ew') self.unit_frame = frame = Frame(guiParent, grid=(1, 1), gridSpan=(1, 2)) self.unitLabel = Label(frame, text='Current units: ', grid=(0, 0)) tipText = 'Selects which unit of measurement to display peak dimension positions with' self.unitSelect = PulldownList(frame, callback=self.changeUnit, texts=units, grid=(0, 1), tipText=tipText) self.heightLabel = Label(guiParent, text='Height', borderwidth=2, relief='groove') tipText = 'Sets the peak height; the value of the spectrum point intensity (albeit often interpolated)' self.heightEntry = FloatEntry(guiParent, borderwidth=1, tipText=tipText) self.volumeLabel = Label(guiParent, text='Volume', borderwidth=2, relief='groove') tipText = 'Sets the peak volume integral; normally a summation of data point values' self.volumeEntry = FloatEntry(guiParent, borderwidth=1, tipText=tipText) self.detailLabel = Label(guiParent, text='Details', borderwidth=2, relief='groove') tipText = 'A user-configurable textual comment for the peak, which appears an tables and occasionally on spectrum displays' self.detailEntry = Entry(guiParent, borderwidth=1, tipText=tipText) tipTexts = [ 'Commits the specified values to update the peak and closes the popup', ] texts = ['Update'] commands = [self.commit] self.buttons = UtilityButtonList(guiParent, texts=texts, commands=commands, doClone=False, helpUrl=self.help_url, tipTexts=tipTexts) def open(self): self.updatePeak() BasePopup.open(self) def updatePeak(self, object=None): peak = None if object: if object.className == 'Peak': peak = object elif object.className == 'PeakDim': peak = object.peak elif object.className == 'PeakIntensity': peak = object.peak if (peak is None) or (peak is self.peak): self.update(peak=self.peak) def update(self, peak=None, peakList=None): # first destroy old labels and entries (saves grid hassles) for label in self.dimensionLabels: label.destroy() for entry in self.dimensionEntries: entry.destroy() # now setup required data if peak: title = 'Edit Peak' self.buttons.buttons[0].config(text='Update') else: title = 'Add Peak' self.buttons.buttons[0].config(text='Add Peak') self.setTitle(title) self.peak = peak self.peakList = peakList if not peakList: if peak: self.peakList = peak.peakList else: return peakList = self.peakList spectrum = peakList.dataSource.name self.numDims = peakList.dataSource.numDim self.posn = self.numDims * [0] self.dataDims = peakList.dataSource.sortedDataDims() if self.peak: serial = self.peak.serial dims = self.peak.sortedPeakDims() details = self.peak.details if not details: details = '' if self.peak.annotation: annotn = '%0.16s' % self.peak.annotation else: annotn = '' heightIntensity = self.peak.findFirstPeakIntensity( intensityType='height') volumeIntensity = self.peak.findFirstPeakIntensity( intensityType='volume') if heightIntensity: height = heightIntensity.value else: height = 0.0 if volumeIntensity: volume = volumeIntensity.value else: volume = 0.0 for i in range(self.numDims): peakDim = dims[i] dataDimRef = peakDim.dataDimRef if dataDimRef: self.posn[i] = peakDim.position + ( peakDim.numAliasing * dataDimRef.dataDim.numPointsOrig) else: self.posn[i] = peakDim.position else: dict = peakList.__dict__.get('serialDict') if dict is None: serial = 1 else: serial = dict.get('peaks', 0) + 1 height = 0.0 volume = 0.0 details = '' annotn = '' self.specLabel.set( text='Experiment: %s Spectrum: %s PeakList: %d' % (peakList.dataSource.experiment.name, spectrum, peakList.serial)) self.peakLabel.set(text='Peak: %d' % serial) self.dimensionLabels = self.numDims * [''] self.dimensionEntries = self.numDims * [''] for i in range(self.numDims): pos = self.posn[i] if self.unit != 'point': dataDim = self.dataDims[i] if dataDim.className == 'FreqDataDim': pos = unit_converter[('point', self.unit)]( pos, getPrimaryDataDimRef(dataDim)) self.dimensionLabels[i] = Label(self.master_frame, text='F%d' % (i + 1), borderwidth=2, relief='groove') tipText = 'The peak position in dimension %d, in the specified units' % ( i + 1) self.dimensionEntries[i] = FloatEntry(self.master_frame, borderwidth=1, text='%8.4f' % pos, tipText=tipText) self.heightEntry.set(text='%f' % height) self.volumeEntry.set(text='%f' % volume) self.detailEntry.set(text=details) row = 0 self.specLabel.grid(row=row, column=0, columnspan=2, sticky='nsew') row = row + 1 self.peakLabel.grid(row=row, column=0, sticky='nsew') self.unit_frame.grid(row=row, column=1, columnspan=2, sticky='nsew') for i in range(self.numDims): row = row + 1 self.dimensionLabels[i].grid(row=row, column=0, sticky='nsew') self.dimensionEntries[i].grid(row=row, column=1, columnspan=3, sticky='e') row = row + 1 self.heightLabel.grid(row=row, column=0, sticky='nsew') self.heightEntry.grid(row=row, column=1, columnspan=3, sticky='e') row = row + 1 self.volumeLabel.grid(row=row, column=0, sticky='nsew') self.volumeEntry.grid(row=row, column=1, columnspan=3, sticky='e') row = row + 1 self.detailLabel.grid(row=row, column=0, sticky='nsew') self.detailEntry.grid(row=row, column=1, columnspan=3, sticky='e') row = row + 1 self.buttons.grid(row=row, column=0, columnspan=4, sticky='nsew') def changeUnit(self, unit): posDisp = self.numDims * [None] for i in range(self.numDims): posDisp[i] = float(self.dimensionEntries[i].get()) if self.unit != 'point': dataDim = self.dataDims[i] if dataDim.className == 'FreqDataDim': posDisp[i] = unit_converter[(self.unit, 'point')]( posDisp[i], getPrimaryDataDimRef(dataDim)) self.unit = unit if self.unit != 'point': for i in range(self.numDims): dataDim = self.dataDims[i] if dataDim.className == 'FreqDataDim': posDisp[i] = unit_converter[('point', self.unit)]( posDisp[i], getPrimaryDataDimRef(dataDim)) for i in range(self.numDims): value = posDisp[i] if value is None: self.dimensionEntries[i].set('None') else: self.dimensionEntries[i].set('%8.4f' % posDisp[i]) def commit(self): posDisp = self.numDims * [0] for i in range(self.numDims): posDisp[i] = float(self.dimensionEntries[i].get()) if self.unit != 'point': dataDim = self.dataDims[i] if dataDim.className == 'FreqDataDim': self.posn[i] = unit_converter[(self.unit, 'point')]( posDisp[i], getPrimaryDataDimRef(dataDim)) else: self.posn[i] = posDisp[i] if self.peak: movePeak(self.peak, self.posn) else: self.peak = pickPeak(self.peakList, self.posn) height = self.heightEntry.get() volume = self.volumeEntry.get() setManualPeakIntensity(self.peak, height, intensityType='height') setManualPeakIntensity(self.peak, volume, intensityType='volume') details = self.detailEntry.get() or None self.peak.setDetails(details) self.close() def deletedPeak(self, peak): if self.peak is peak: self.close() def destroy(self): self.unregisterNotify(self.deletedPeak, 'ccp.nmr.Nmr.Peak', 'delete') for func in ('setAnnotation', 'setDetails', 'setFigOfMerit'): self.unregisterNotify(self.updatePeak, 'ccp.nmr.Nmr.Peak', func) for func in ('setAnnotation', 'setPosition', 'setNumAliasing'): self.unregisterNotify(self.updatePeak, 'ccp.nmr.Nmr.PeakDim', func) for func in ('__init__', 'delete', 'setValue'): self.unregisterNotify(self.updatePeak, 'ccp.nmr.Nmr.PeakIntensity', func) BasePopup.destroy(self)
class EditMeasurementListsPopup(BasePopup): """ **The NMR Derived Measurements and Their Lists** The purpose of this popup window is to display lists of measurements within the current CCPN project. A "Measurement" in this regard is a value that is derived from NMR data and connects to one or more resonances (which may be assigned to atoms). The most commonly encountered kind of measurement is the chemical shift, which is usually derived from recording the positions of peaks in spectra. In this instance the chemical shift measurement of a resonance is made when it is assigned to the dimension of a peak. Because CCPN allows multiple shift lists (technically a type of measurement list) a resonance may have a several chemical shift measurements; useful studying different conditions. There are several different kinds of measurement and hence measurement list that may be included within a CCPN project, for example J-coupling, hydrogen exchange rate, chemical shift anisotropy, T1 relaxation and chemical shift. All types will be displayed by this popup if they are available within the current project, although Analysis does not necessarily have tools to record all of these different kinds of measurement. The layout of this popup consists of two tabs, one for the organising lists that contain the measurements and another to display the individual measurements within a single list. With the exception of blank lists for chemical shifts, measurement lists are not made using this popup, instead they are made at the point where an analysis is performed, e.g. T1 lists are made with data from the `Follow Intensity Changes`_ tool. The second tab, containing the "Measurements" table displays the details of the individual measured values within a selected list. What the values mean and which units they are in, if any, naturally depends on the kind of list being viewed. If measurements are made within CCPN software (as opposed to being imported) then the individual measurements usually record the spectrum peaks that were used in the calculation of the measured value, e.g. the peaks for which positions record chemical shift or intensities record T1. **Caveats & Tips** If there is no chemical shift list within a project a new one will be made automatically to record the shifts of any assignments. The shift list with which an experiment is associate may be changed via the "Shift List" column of the main `Experiments`_ table. Measurements other than chemical shift, like T1 and T2 relaxation times will only appear within this system if a measurement list entity is formally made. Simply measuring values from spectra, for example using the `Follow Intensity Changes`_ tool .. _`Follow Intensity Changes`: CalcRatesPopup.html .. _`Experiments`: EditExperimentPopup.html """ def __init__(self, parent, *args, **kw): self.guiParent = parent self.experiment = None self.measurementList = None self.measurementListB = None self.measurement = None self.waitingList = False self.waitingMeasurement = False BasePopup.__init__(self, parent, title="Resonance : Measurement Lists", **kw) def body(self, guiFrame): self.geometry('600x600') guiFrame.expandGrid(0, 0) tipTexts = [ 'A table of all the NMR measurement lists in the project, including shift lists, T1 lists, J-coupling lists etc.', 'A table listing all of the individual measurements within an NMR measurement list' ] options = ['Measurement Lists', 'Measurements Table'] tabbedFrame = TabbedFrame(guiFrame, options=options, grid=(0, 0), tipTexts=tipTexts) self.tabbedFrame = tabbedFrame frameA, frameB = tabbedFrame.frames # Measurement Lists frameA.expandGrid(1, 0) self.detailsEntry = Entry(self, text='', returnCallback=self.setDetails, width=12) self.nameEntry = Entry(self, text='', returnCallback=self.setName, width=12) row = 0 frame0 = Frame(frameA, grid=(row, 0), gridSpan=(1, 2)) frame0.grid_columnconfigure(2, weight=1) label = Label(frame0, text='Experiment:', grid=(0, 0), sticky='e') tipText = 'Selects an experiment, if required, to restrict the measurement list table display; showing only lists which were derived using the experiment' self.experimentPulldown = PulldownList(frame0, callback=self.setExperiment, grid=(0, 1), tipText=tipText) row += 1 tipTexts = [ 'The serial number of measurement list', 'The type of measurement list, e.g. shift list, T1 list, J coupling list', 'A short identifying name for the list, for graphical displays', 'The number of measurements contained within the list', 'The unit of measurement used for the values in the list', 'The names of the experiments which were used to derive the measurements', 'A user-specified textual comment for the measurement list' ] justifyList = [ 'center', 'center', 'center', 'center', 'center', 'left' ] #colHeadings = ['List Type','Name','Size','Unit','Experiments','Other Info','Details'] colHeadings = [ '#', 'List Type', 'Name', 'Size', 'Unit', 'Experiments', 'Details' ] editWidgets = [ None, None, self.nameEntry, None, None, None, None, self.detailsEntry ] editGetCallbacks = [ None, None, self.getName, None, None, None, None, self.getDetails ] editSetCallbacks = [ None, None, self.setName, None, None, None, None, self.setDetails ] self.listsMatrix = ScrolledMatrix( frameA, grid=(row, 0), gridSpan=(1, 2), justifyList=justifyList, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, headingList=colHeadings, callback=self.selectListCell, deleteFunc=self.deleteMeasurementList, tipTexts=tipTexts) row += 1 tipTexts = [ 'Show a table of the individual measurements within the selected measurement list', 'Make a new, blank chemical shift list within the project', 'Make a synthetic chemical shift list using random coil values, adjusting protein backbone values for sequence where approprate', 'Delete the selected measurement list' ] texts = [ 'Show Measurements', 'New Shift List', 'Make Random Coil Shift List', 'Delete' ] commands = [ self.showMeasurements, self.addShiftList, self.makeRandomCoilShiftList, self.deleteMeasurementList ] self.listButtons = ButtonList(frameA, texts=texts, commands=commands, grid=(row, 0), gridSpan=(1, 2), tipTexts=tipTexts) # Measurements self.measurementDetailsEntry = Entry( self, text='', returnCallback=self.setMeasurementDetails, width=12) self.measurementMeritEntry = FloatEntry( self, returnCallback=self.setMeasurementMerit, width=6) row = 0 frame0 = Frame(frameB, grid=(row, 0)) frame0.grid_columnconfigure(2, weight=1) label = Label(frame0, text='Measurement List:', grid=(0, 0), sticky='e') tipText = 'Selects the measurement list to display measurements for' self.listPulldown = PulldownList(frame0, callback=self.setMeasurementList, grid=(0, 1), tipText=tipText) row += 1 frameB.expandGrid(row, 0) tipTexts = [ 'The serial number of the measurement within its containing list', 'The number or assignment of the NMR resonance(s) to which the measurement applies', 'The numeric value of the NMR measurement on the specified resonance(s), and the unit of measurement', 'The standard deviation error in the measured value', 'The molecular chain, if any, to which the measurement relates by virtue of atom assigned resonances', 'The isotope type(s) of the measures resonance(s)', 'A figure-of-merit value for the measurement indicating its quality or reliability', 'The number of peaks in the CCPN project used to take the measurement', 'A user-defined textual comment for the measurement' ] justifyList = [ 'center', 'center', 'center', 'center', 'center', 'center', 'center', 'center', 'center', 'left' ] colHeadings = [ '#', 'Resonance', 'Value', 'SD', 'Chain', 'Isotope', 'Fig of\nMerit', 'Peaks', 'Details' ] editWidgets = [ None, None, None, None, None, None, self.measurementMeritEntry, None, self.measurementDetailsEntry ] editGetCallbacks = [ None, None, None, None, None, None, self.getMeasurementMerit, self.showPeaks, self.getMeasurementDetails ] editSetCallbacks = [ None, None, None, None, None, None, self.setMeasurementMerit, None, self.setMeasurementDetails ] self.measurementsMatrix = ScrolledMatrix( frameB, grid=(row, 0), multiSelect=True, tipTexts=tipTexts, justifyList=justifyList, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, headingList=colHeadings, callback=self.selectMeasurementCell) row += 1 tipTexts = [ 'Show a table containing peaks that were used to derive the selected measurements', 'For some measurement lists (currently only shift lists) manually trigger a recalculation of values', 'Show a table containing the resonances that relate to the selected measurements', 'Delete the selected measurement records; cannot be done for chemical shift values still ties to peaks via assignment' ] texts = ['Show Peaks', 'Recalculate', 'Show Resonances', 'Delete'] commands = [ self.showPeaks, self.recalculateMeasurements, self.showResonances, self.deleteMeasurements ] self.measurementButtons = ButtonList(frameB, texts=texts, grid=(row, 0), commands=commands, tipTexts=tipTexts) # Main Frame self.bottomButtons = UtilityButtonList(tabbedFrame.sideFrame, helpUrl=self.help_url, grid=(0, 0), gridSpan=(1, 2), sticky='e') self.updateMeasurementListAfter() self.updateMeasurementsAfter() self.administerNotifiers(self.registerNotify) def administerNotifiers(self, notifyFunc): for func in ('__init__', 'delete', 'setName', 'setDipolarRelaxList', 'setHExchProtectionList', 'setHExchRateList', 'setNoeList', 'setJCouplingList', 'setRdcList', 'setShiftAnisotropyList', 'setShiftDifferenceList', 'setShiftList', 'setT1List', 'setT1RhoList', 'setT2List'): for clazz in ('ccp.nmr.Nmr.Experiment', ): notifyFunc(self.updateExperiments, clazz, func) for func in ('__init__', 'delete', 'setDetails', 'setName', 'addExperiment', 'removeExperiment', 'setExperiments'): for clazz in ('ccp.nmr.Nmr.DipolarRelaxList', 'ccp.nmr.Nmr.HExchProtectionList', 'ccp.nmr.Nmr.HExchRateList', 'ccp.nmr.Nmr.NoeList', 'ccp.nmr.Nmr.JCouplingList', 'ccp.nmr.Nmr.RdcList', 'ccp.nmr.Nmr.ShiftAnisotropyList', 'ccp.nmr.Nmr.ShiftDifferenceList', 'ccp.nmr.Nmr.ShiftList', 'ccp.nmr.Nmr.T1List', 'ccp.nmr.Nmr.T1RhoList', 'ccp.nmr.Nmr.T2List'): notifyFunc(self.updateMeasurementListAfter, clazz, func) # Measurements for func in ('__init__', 'delete'): for clazz in ('ccp.nmr.Nmr.DipolarRelaxation', 'ccp.nmr.Nmr.HExchProtection', 'ccp.nmr.Nmr.HExchRate', 'ccp.nmr.Nmr.Noe', 'ccp.nmr.Nmr.JCoupling', 'ccp.nmr.Nmr.Rdc', 'ccp.nmr.Nmr.Shift', 'ccp.nmr.Nmr.ShiftAnisotropy', 'ccp.nmr.Nmr.ShiftDifference', 'ccp.nmr.Nmr.T1', 'ccp.nmr.Nmr.T1Rho', 'ccp.nmr.Nmr.T2'): notifyFunc(self.updateMeasurementAfter, clazz, func) for func in ('__init__', 'delete', 'setDetails', 'setError', 'setFigOfMerit', 'setValue', 'setPeaks', 'addPeak', 'removePeak'): for clazz in ('ccp.nmr.Nmr.DipolarRelaxation', 'ccp.nmr.Nmr.HExchProtection', 'ccp.nmr.Nmr.HExchRate', 'ccp.nmr.Nmr.Noe', 'ccp.nmr.Nmr.JCoupling', 'ccp.nmr.Nmr.Rdc', 'ccp.nmr.Nmr.Shift', 'ccp.nmr.Nmr.ShiftAnisotropy', 'ccp.nmr.Nmr.ShiftDifference', 'ccp.nmr.Nmr.T1', 'ccp.nmr.Nmr.T1Rho', 'ccp.nmr.Nmr.T2'): self.registerNotify(self.updateMeasurementAfter, clazz, func) def open(self): self.updateMeasurementListAfter() self.updateMeasurementsAfter() BasePopup.open(self) def makeRandomCoilShiftList(self): molSystems = self.project.molSystems shiftList = makeRandomCoilShiftList(molSystems) def addShiftList(self): shiftList = newShiftList(self.project) self.setExperiment(None) def getName(self, measurementList): if measurementList: self.nameEntry.set(measurementList.name) def setName(self, event): text = self.nameEntry.get() if text and text != ' ': self.measurementList.setName(text) def getDetails(self, measurementList): if measurementList and measurementList.details: self.detailsEntry.set(measurementList.details) def setDetails(self, event): text = self.detailsEntry.get() if text and text != ' ': self.measurementList.setDetails(text) def setExperiment(self, experiment): if experiment is not self.experiment: self.experiment = experiment self.measurementList = None self.updateMeasurementListAfter() def deleteMeasurementList(self, *event): if self.measurementList: if (len(self.measurementList.experiments) > 0) and (self.measurementList.className == 'ShiftList'): showWarning( 'Warning', 'Deletion of shift lists with associated experiments prohibited', parent=self) return elif len(self.measurementList.measurements) > 0: if showOkCancel('Confirm', 'List is not empty. Really delete?', parent=self): self.measurementList.delete() self.measurementList = None else: return else: self.measurementList.delete() self.measurementList = None def showMeasurements(self): self.updateMeasurements(self.measurementList) self.tabbedFrame.select(1) def selectListCell(self, object, row, col): self.measurementList = object if self.measurementList: self.listButtons.buttons[0].enable() self.listButtons.buttons[3].enable() self.setMeasurementList(self.measurementList) def updateMeasurementAfter(self, measurement): if (self.experiment is None) or ( self.experiment in measurement.parentList.experiments): self.updateMeasurementListAfter() if measurement.parentList is self.measurementListB: self.updateMeasurementsAfter() def updateMeasurementListAfter(self, measurementList=None): self.updateListPulldown() if self.waitingList: return if (measurementList is None) \ or (self.experiment is None) \ or (self.experiment in measurementList.experiments): self.waitingList = True self.after_idle(self.updateListsTable) def updateExperiments(self, null=None): index = 0 experiments = [ None, ] + self.nmrProject.sortedExperiments() names = [ '<Any>', ] + [e.name for e in experiments[1:]] experiment = self.experiment if experiments: if experiment not in experiments: experiment = experiments[0] index = experiments.index(experiment) else: experiment = None if self.experiment is not experiment: self.experiment = experiment self.updateMeasurementListAfter() self.experimentPulldown.setup(names, experiments, index) def updateListsTable(self, experiment=None): if experiment is None: experiment = self.experiment else: self.experiment = experiment if self.experiment is None: self.measureList = None self.updateExperiments() if self.measurementList: self.listButtons.buttons[0].enable() self.listButtons.buttons[3].enable() else: self.listButtons.buttons[0].disable() self.listButtons.buttons[3].disable() if self.experiment == None: measurementLists = self.nmrProject.sortedMeasurementLists() else: measurementLists = [] lists = self.nmrProject.sortedMeasurementLists() for measurementList in lists: if self.experiment in measurementList.experiments: measurementLists.append(measurementList) objectList = [] textMatrix = [] for measurementList in measurementLists: objectList.append(measurementList) names = ['%s' % (e.name) for e in measurementList.experiments] if len(names) > 6: names.insert(int(len(names) / 2), '\n') experimentText = ' '.join(names) """otherInfoText = '' for attribute in measurementList.metaclass.getAllAttributes(): name = attribute.name otherInfoText += '%s:%s ' % (name,getattr(measurementList, name)) for role in measurementList.metaclass.getAllRoles(): name = role.name otherInfoText += '%s:%s ' % (name,getattr(measurementList, name))""" datum = [ measurementList.serial, measurementList.className, measurementList.name, len(measurementList.measurements), measurementList.unit, experimentText, #otherInfoText, measurementList.details ] textMatrix.append(datum) if not objectList: textMatrix.append([]) self.listsMatrix.update(objectList=objectList, textMatrix=textMatrix) self.waitingList = False def destroy(self): self.administerNotifiers(self.unregisterNotify) BasePopup.destroy(self) # Measurement functions def deleteMeasurements(self): measurements = self.measurementsMatrix.currentObjects if measurements: className = self.measurementListB.className if className == 'ShiftList': msg = 'Really delete %s chemical shifts?' % len(measurements) if not showOkCancel('Confirm', msg, parent=self): return untouched = set() for shift in measurements: for peakDim in shift.peakDims: shiftList = peakDim.peak.peakList.dataSource.experiment.shiftList if shiftList is self.measurementListB: untouched.add(shift) break else: shift.delete() if untouched: if len(untouched) > 1: msg = 'Could not delete %d shifts because they are still assigned to peaks' % len( untouched) else: msg = 'Could not delete shift because it is still assigned to peaks' showWarning('Warning', msg, parent=self) else: msg = 'Really delete %d %s measurements?' % (len(measurements), className[:-4]) if not showOkCancel('Confirm', msg, parent=self): return for measurement in measurements: measurement.delete() def recalculateMeasurements(self): measurements = self.measurementsMatrix.currentObjects if measurements and (self.measurementListB.className == 'ShiftList'): for shift in measurements: averageShiftValue(shift) def showPeaks(self, *event): measurements = self.measurementsMatrix.currentObjects if measurements: peaksDict = {} for measurement in measurements: for peak in measurement.peaks: peaksDict[peak] = 1 peaks = peaksDict.keys() if len(peaks) > 0: self.guiParent.viewPeaks(peaks) def showResonances(self, *event): measurements = self.measurementsMatrix.currentObjects if measurements: resonances = set() for measurement in measurements: if hasattr(measurement, 'resonances'): resonances.update(measurement.resonances) else: resonances.add(measurement.resonance) if resonances: self.guiParent.viewSelectedResonances(resonances) def setMeasurementList(self, measurementList): if measurementList is not self.measurementListB: self.measurementListB = measurementList self.updateMeasurementsAfter() def getMeasurementMerit(self, measurement): if measurement: self.measurementMeritEntry.set(measurement.figOfMerit) def setMeasurementMerit(self, event): value = self.measurementMeritEntry.get() if value is not None: self.measurement.setFigOfMerit(max(0.0, min(1.0, float(value)))) def getMeasurementDetails(self, measurement): if measurement and measurement.details: self.measurementDetailsEntry.set(measurement.details) def setMeasurementDetails(self, event): text = self.measurementDetailsEntry.get() if text and text != ' ': self.measurement.setDetails(text) def selectMeasurementCell(self, object, row, col): self.measurement = object if self.measurement: self.measurementButtons.buttons[0].enable() self.measurementButtons.buttons[2].enable() if self.measurementListB.className == 'ShiftList': self.measurementButtons.buttons[1].enable() else: self.measurementButtons.buttons[1].disable() def getMeasurementListName(self, measurementList): name = measurementList.name or \ '%d:%s' % (measurementList.serial,measurementList.className) return name def updateListPulldown(self, null=None): index = 0 measurementLists = self.nmrProject.sortedMeasurementLists() nameFunc = self.getMeasurementListName names = [nameFunc(ml) for ml in measurementLists] measurementList = self.measurementListB if measurementLists: if measurementList not in measurementLists: measurementList = measurementLists[0] index = measurementLists.index(measurementList) else: measurementList = None if self.measurementListB is not measurementList: self.measurementListB = measurementList self.updateMeasurementsAfter() self.listPulldown.setup(names, measurementLists, index) def updateMeasurementsAfter(self, *opt): if self.waitingMeasurement: return else: self.waitingMeasurement = True self.after_idle(self.updateMeasurements) def updateMeasurements(self, measurementList=None): headingList = [ '#', 'Resonance', 'Value', 'SD', 'Chain', 'Isotope', 'Fig of\nMerit', 'Peaks', 'Details' ] if measurementList is not None: self.measurementListB = measurementList self.updateListPulldown() if self.measurementListB is None: self.measurement = None measurements = [] else: if self.measurementListB.unit: headingList[2] = 'Value\n(%s)' % (self.measurementListB.unit) measurements = self.measurementListB.sortedMeasurements() if measurements and hasattr(measurements[0], 'resonances'): headingList[1] = 'Resonances' headingList[5] = 'Isotopes' if self.measurement: self.measurementButtons.buttons[0].enable() self.measurementButtons.buttons[2].enable() if self.measurementListB.className == 'ShiftList': self.measurementButtons.buttons[1].enable() else: self.measurementButtons.buttons[1].disable() else: self.measurementButtons.buttons[0].disable() self.measurementButtons.buttons[1].disable() self.measurementButtons.buttons[2].disable() objectList = [] textMatrix = [] i = 0 for measurement in measurements: i += 1 resonanceText = '' isotopeText = '' chainText = '' if hasattr(measurement, 'resonances'): resonanceText = ' '.join( [makeResonanceGuiName(r) for r in measurement.resonances]) isotopeText = ' '.join( [r.isotopeCode for r in measurement.resonances]) residueDict = {} for resonance in measurement.resonances: if resonance.resonanceSet: residue = resonance.resonanceSet.findFirstAtomSet( ).findFirstAtom().residue residueDict[residue] = None keys = residueDict.keys() if len(keys) == 1: residue = keys[0] residueNum = residue.seqCode chainText = '%s:%s' % (residue.chain.molSystem.code, residue.chain.code) elif hasattr(measurement, 'resonance'): resonance = measurement.resonance resonanceText = makeResonanceGuiName(resonance) isotopeText = measurement.resonance.isotopeCode if resonance.resonanceSet: residue = resonance.resonanceSet.findFirstAtomSet( ).findFirstAtom().residue chainText = '%s:%s' % (residue.chain.molSystem.code, residue.chain.code) datum = [ i, resonanceText, measurement.value, measurement.error, chainText, isotopeText, measurement.figOfMerit, len(measurement.peaks), measurement.details or ' ' ] objectList.append(measurement) textMatrix.append(datum) if not objectList: textMatrix.append([]) tipTexts = self.measurementsMatrix.tipTexts # unchanged, despite variable headings self.measurementsMatrix.update(headingList=headingList, objectList=objectList, textMatrix=textMatrix, tipTexts=tipTexts) self.waitingMeasurement = False
class EditResStructuresPopup(BasePopup): def __init__(self, parent, *args, **kw): self.guiParent = parent self.structure = None self.constraintSet = None self.cloud = None self.cloudRmsdDict = {} self.strucRmsdDict = {} self.waiting = False BasePopup.__init__(self, parent=parent, title="Resonance Cloud Structures", **kw) def body(self, guiFrame): row = 0 guiFrame.grid_columnconfigure(1, weight=1) guiFrame.grid_rowconfigure(0, weight=0) guiFrame.grid_rowconfigure(1, weight=1) self.generationLabel = Label(guiFrame, text='Structure Generation:') constraintSets = [] constraintSetNames = [] index = -1 for constraintSet in self.project.nmrConstraintStores: index += 1 constraintSets.append(constraintSet) constraintSetNames.append(str(constraintSet.serial)) self.constraintSet = constraintSet self.constrSetPulldown = PulldownMenu(guiFrame, self.changeConstraintSet, constraintSetNames, selected_index=index, do_initial_callback=False) self.generationLabel.grid(row=row, column=0, columnspan=1, sticky='e') self.constrSetPulldown.grid(row=row, column=1, columnspan=1, sticky='w') strucLabel = Label(guiFrame, text='Comparison structure') strucLabel.grid(row=row, column=2, sticky='e') self.strucPulldown = PulldownMenu(guiFrame, entries=self.getStructures(), callback=self.setStructure, selected_index=0, do_initial_callback=False) self.strucPulldown.grid(row=row, column=3, sticky='w') sdTolLabel = Label(guiFrame, text='Tolerance (SDs):') sdTolLabel.grid(row=row, column=4, sticky='e') self.sdToleranceEntry = FloatEntry(guiFrame, text=2.0, width=6) self.sdToleranceEntry.grid(row=row, column=5, stick='w') row += 1 colHeadings = ['#', 'File name', 'RMSD to mean', 'RMSD to structure'] self.scrolledMatrix = ScrolledMatrix(guiFrame, initialRows=10, headingList=colHeadings, callback=self.selectCell, objectList=[], textMatrix=[ [], ]) self.scrolledMatrix.grid(row=row, column=0, columnspan=6, sticky='nsew') row += 1 texts = [ 'Calc\nRMSD', 'Make Cloud\nfrom structure', 'Delete', 'Delete\nbad' ] commands = [ self.calcRmsd, self.makeStrucCloud, self.deleteCloud, self.filterClouds ] self.bottomButtons = UtilityButtonList(guiFrame, texts=texts, expands=False, commands=commands, helpUrl=self.help_url) self.bottomButtons.grid(row=row, column=0, columnspan=6, sticky='nsew') self.update() for func in ('__init__', 'delete'): self.registerNotify(self.updateStructureGen, 'ccp.nmr.Nmr.StructureGeneration', func) def open(self): self.updateAfter() BasePopup.open(self) def getStructures(self): names = [ '<None>', ] for molSystem in self.project.sortedMolSystems(): for structure in molSystem.sortedStructureEnsembles(): names.append('%s:%d' % (molSystem.name, structure.ensembleId)) return names def setStructure(self, index, name=None): if index < 1: self.structure = None else: structures = [] for molSystem in self.project.sortedMolSystems(): for structure in molSystem.sortedStructureEnsembles(): structures.append(structure) self.structure = structures[index - 1] def filterClouds(self): if self.constraintSet: sdTolerance = self.sdToleranceEntry.get() or 2.0 keptClouds = [] clouds = self.guiParent.application.getValues( self.constraintSet, 'clouds') meanGroupRmsds = [] for cloud in clouds: rmsd = self.cloudRmsdDict.get(cloud) if rmsd is not None: meanGroupRmsds.append(rmsd) meanRmsd = 0.0 N = 0 for rmsd in meanGroupRmsds: meanRmsd += rmsd N += 1 if N > 0: meanRmsd /= float(N) sd = 0.0 for rmsd in meanGroupRmsds: sd += (rmsd - meanRmsd) * (rmsd - meanRmsd) if N > 0: sd /= float(N - 1) sd = sqrt(sd) print meanRmsd, '+/-', sd n = 0 for cloud in clouds: rmsd = self.cloudRmsdDict.get(cloud) if rmsd is None: keptClouds.append(cloud) elif abs(rmsd - meanRmsd) > (sdTolerance * sd): print 'Cloud %s is bad' % (cloud) else: keptClouds.append(cloud) self.guiParent.application.setValues(self.constraintSet, 'clouds', values=keptClouds) self.updateAfter() def makeStrucCloud(self): if self.structure: serials = self.guiParent.application.getValues( self.constraintSet, 'cloudsResonances') pdbFileName = 'CloudForStructure.pdb' #from ccpnmr.clouds.AtomCoordList import AtomCoordList from ccpnmr.c.AtomCoordList import AtomCoordList atomCoordList = AtomCoordList() resDict = {} hmass = 25 print "L1", len(serials) for resonance in self.nmrProject.resonances: resDict[resonance.serial] = resonance print "L2", len(resDict) resonances = [] for serial in serials: if resDict.get(serial) is not None: resonances.append(resDict[serial]) print "L3", len(resonances) C = 0 for resonance in resonances: resonanceSet = resonance.resonanceSet if resonanceSet: i = resonanceSet.sortedResonances().index(resonance) atomSet = resonance.resonanceSet.sortedAtomSets()[i] coords = getAtomSetCoords(atomSet, self.structure) coord = coords[0] atomCoordList.add(hmass, coord.x, coord.y, coord.z) C += 1 print "L4", len(atomCoordList) from ccpnmr.clouds.FilterClouds import writeTypedPdbCloud writeTypedPdbCloud(atomCoordList, pdbFileName, resonances) clouds = self.guiParent.application.getValues( self.constraintSet, 'clouds') clouds.append(pdbFileName) self.guiParent.application.setValues(self.constraintSet, 'clouds', values=clouds) self.updateAfter() def calcRmsd(self): if self.constraintSet: clouds = self.guiParent.application.getValues( self.constraintSet, 'clouds') from ccpnmr.clouds.FilterClouds import filterClouds rmsds = filterClouds(clouds) n = len(clouds) for i in range(n): cloud = clouds[i] rmsd = rmsds[i] self.cloudRmsdDict[cloud] = rmsd self.updateAfter() def changeConstraintSet(self, i, name): project = self.project if project.nmrConstraintStores: constraintSet = project.nmrConstraintStores[i] else: constraintSet = None if constraintSet is not self.constraintSet: self.constraintSet = constraintSet self.cloud = None self.updateAfter() def deleteCloud(self): if self.constraintSet and self.cloud and showOkCancel( 'Confirm', 'Really delete resonance cloud?', parent=self): clouds = self.guiParent.application.getValues( self.constraintSet, 'clouds') if clouds: clouds.remove(self.cloud) self.cloud = None self.guiParent.application.setValues(self.constraintSet, 'clouds', values=clouds) self.updateAfter() def selectCell(self, cloud, row, col): self.cloud = cloud self.bottomButtons.buttons[1].enable() def updateAfter(self, *opt): if self.waiting: return else: self.waiting = True self.after_idle(self.update) def getConstraintSetNames(self): names = [] constraintSets = self.project.nmrConstraintStores for set in constraintSets: names.append('%d' % set.serial) return names def updateStructureGen(self, *opt): project = self.project constraintSets = self.project.sortedNmrConstraintStores if constraintSets: constraintSetNames = self.getConstraintSetNames() # set defaults if self.constraintSet not in constraintSets: self.constraintSet = constraintSets[0] self.cloud = None i = constraintSets.index(self.constraintSet) self.constrSetPulldown.setup(constraintSetNames, i) else: self.constraintSet = None self.cloud = None self.constrSetPulldown.setup([], -1) def destroy(self): for func in ('__init__', 'delete'): self.unregisterNotify(self.updateStructureGen, 'ccp.nmr.Nmr.StructureGeneration', func) BasePopup.destroy(self) def update(self): objectList = [] textMatrix = [] if self.constraintSet: clouds = self.guiParent.application.getValues( self.constraintSet, 'clouds') if clouds: objectList = list(clouds) i = 0 for cloud in objectList: i += 1 datum = [] datum.append(i) datum.append(cloud) datum.append(self.cloudRmsdDict.get(cloud) or '-') datum.append(self.strucRmsdDict.get(cloud) or '-') textMatrix.append(datum) if not self.cloud: self.bottomButtons.buttons[1].disable() self.scrolledMatrix.update(objectList=objectList, textMatrix=textMatrix) self.waiting = False
class IsotopeSchemeEditor(BasePopup): """ **Create and Edit Per-residue Reference Isotope Schemes** This system allows the user to create schemes that describe particular patterns of atomic isotope labelling in terms of combinations of isotopically labelled forms of residues. Once constructed, these schemes may then be applied to a molecule of known residue sequence to gauge the levels of spin-active isotope incorporation in an NMR experiment. This information is useful in several places withing Analysis, including giving more intelligent assignment options and in the generation of distance restraints by matching peak positions to chemical shifts. Although the schemes may be used directly they are typically used as reference information for configuring the `Isotope Labelling`_ system; where isotope labelling patterns are tied to particular molecules and experiments. Because all of the different isotope labelled versions (isotopomers) of each residue type are described independently, a scheme can be used to estimate the specific amounts of incorporation present at multiple atom sites at the same time. For example, although a residue type may have significant levels of 13C at the CA and CB positions on average, there may be no form of the residue where CA and CB are labelled at the same time, and thus CA-CB correlations would not be observed in NMR. This popup window is divided into three main tabs, the first describes the overall schemes that are available; that would be applied to a molecule in a given situation. The second tab details the residue isotopomer components within the selected scheme, i.e. which labelled residue forms are present. The last tab displays isotopomer labelling in a graphical, three-dimensional way. If any isotope labelling schemes have been created or edited the user may immediately save these to disk via the [Save Schemes] button to the right of the tabs, although these will naturally be saved when the main CCPN project is. **Reference Schemes** This table lists all of the reference isotope schemes that are available to the project. A number of standard schemes are included by default, as part of the main CCPN installation. However, the user is free to create new schemes, either from a completely blank description or by copying and modifying one of the existing schemes. By selecting on a isttope scheme row in the table the scheme is selected to be active for the whole popup and the user can see the contents of the scheme via the other two tabs. It should be noted that the user cannot edit the standard schemes provided by CCPN, given that these are stored with the software. Any new or copied schemes that the user creates will be stored inside the current CCPN project. If a new scheme should be made available to multiple projects, its XML file can be copied into the main CCPN installation, if the user has appropriate write access. **Isotopomers** The middle tab allows the user to view, and where appropriate edit, the isotope labelling descriptions for the residues within the current scheme (selected in the pulldown menu at the top). An isotope scheme is constructed by specifying one or more isotopomers for each residue type. Each isotopomer represents a different way of incorporating spin-active atom labels into a given kind of residue. Often there will only be one labelled form of a residue, and hence one isotopomer. However, with some kinds of isotope enrichment, for example using glycerol 13C labelled at the C2 position, connected labelled and unlabelled atom sites can be incorporated in alternative ways, resulting in distinct forms of labelling patterns that are not the result of a pure random mix. Knowing which labels are present at the same time, in the same isotopomer form, can be very important for determining which NMR correlations are possible. In general use when looking through the default, immutable reference schemes that come with CCPN the user can scroll through the isotopomer versions of each residue in the upper table. By clicking on one of these rows the lower table is filled with details of the amount of each kind of isotope (on average) at each atom site. For the lower "Atom Labels" table only one kind of chemical element is shown at a time, but the user may switch to a different one via the "Chemical Element" pulldown. **Editing Isotopomers** When dealing with copied or new isotope schemes the user is allowed to edit all aspects of the scheme. With a completely new scheme there will be no isotopomer records to start with and it is common practice to fill in a standard set of isotopomers, one for each residue type, made with a base level of isotope incorporation. To set this base level the user can use [Set Default Abundances] to manually specify values, although the default is to use natural abundance levels, which is appropriate in most circumstances. With the base levels set the [Add Default Abundance Set] will automatically fill-in a starting set of isotopomers for the scheme. Extra isotopomers can be added for a specific type of residue via the [Add New:] function and adjacent pulldown menu or by copying existing ones; whichever is easier. Each isotopomer has an editable weight to enable the user to indicate the relative abundance within a given residue type. Once a new isotopomer specification is created clicking on its row allows the user to specify the isotope labelling pattern in the lower "Atom Labels" table. Here the user selects which kind of chemical element to consider and then double-clicks to edit the "Weighting" columns in the table. The weightings represent the relative abundance of a given nuclear isotope at a given atom site. The weightings could be set as ratios, fractions, or percentages; it is only the relative proportion that is important. For example if a carbon atom site was known to have 5% Carbon-12 and 95% Carbon-13 isotopes then the respective weights could be entered as 1 & 19 or 0.05 & 0.95; whatever is most convenient. For efficient setup of schemes the [Propagate Abundances] function can be used to spread the same levels of incorporation over several atom sites (from the last selected row). **Isotopomer Structure** The last tab is an alternative way of presenting the isotope patterns present within the residues of the current scheme (selected in either of the first two tabs). Here the user selects a residue type in the upper left pulldown menu and then a numbered isotopomer, or an average of all isotopomers, in the right hand pulldown menu. The structural display will show a moveable picture of the residue (in a standard conformation) where unlabelled atom sites are represented with grey spheres, labelled sites with yellow spheres and intermediate incorporation with shades in between. It should be noted that this kind of 3D display is only possible if there is an idealised structure available for a residue type. This data will be present for all of the regular biopolymer residues, but may be missing for more unusual compounds; although a lack of coordinates does not impact upon the isotopomer setup. To move and rotate the three-dimensional residue display the following keyboard controls may be used: * Rotate: Arrow keys * Zoom: Page Up & Page Down keys * Translate: Arrow keys + Control key Or alternatively the following mouse controls: * Rotate: Middle button click & drag * Zoom: Mouse wheel or middle button click + Shift key & drag up/down * Translate: Middle button click & drag + Control key Also an options menu appears when the right mouse button is clicked. .. _`Isotope Labelling`: EditMolLabellingPopup.html """ def __init__(self, parent, project=None, *args, **kw): if not project: self.project = Implementation.MemopsRoot( name='defaultUtilityProject') else: self.project = project self.waiting = False self.waitingAtom = False self.molType = 'protein' self.scheme = None self.isotopomer = None self.isotopomerV = False # Not None self.ccpCodeV = None self.element = 'C' self.atomLabelTuple = None self.isotopes = [x[0] for x in getSortedIsotopes(self.project, 'C')] self.defaultAbun = {} BasePopup.__init__(self, parent=parent, title='Molecule : Reference Isotope Schemes', **kw) def body(self, guiFrame): self.geometry('700x600') guiFrame.expandGrid(0, 0) tipTexts = [ 'A table of all of the reference isotope scheme definitions available to the project', 'A list of the residue isotopomers that comprise the selected isotope labelling scheme', 'A three-dimensional representation of residues and their isotopomer labelling' ] options = ['Reference Schemes', 'Isotopomers', 'Isotopomer Structure'] tabbedFrame = TabbedFrame(guiFrame, options=options, grid=(0, 0), tipTexts=tipTexts) self.tabbedFrame = tabbedFrame frameA, frameB, frameC = tabbedFrame.frames # # Schemes # frameA.expandGrid(0, 0) tipTexts = [ 'A short textual code that identifies the reference isotope scheme in graphical displays', 'The full name for the isotope scheme', 'A detailed description of the isotope scheme including user comments', 'The name of the CCPN data repository in which the isotope scheme is saved; "refData" is in the CCPn installation' ] headingList = ['Code', 'Name', 'Description', 'Save Location'] self.schemeNameEntry = Entry(self, text='', returnCallback=self.setSchemeName, width=20) self.schemeDetailsEntry = Entry(self, text='', returnCallback=self.setSchemeDetails, width=20) editWidgets = [ None, self.schemeNameEntry, self.schemeDetailsEntry, None ] editGetCallbacks = [ None, self.getSchemeName, self.getSchemeDetails, None ] editSetCallbacks = [ None, self.setSchemeName, self.setSchemeDetails, None ] self.schemeMatrix = ScrolledMatrix(frameA, headingList=headingList, callback=self.selectScheme, editWidgets=editWidgets, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, multiSelect=False, grid=(0, 0), tipTexts=tipTexts) self.schemeMatrix.doEditMarkExtraRules = self.schemeEditRules tipTexts = [ 'Make a new reference isotope scheme definition based on a copy of the scheme currently selected', 'Delete the selected isotope scheme', 'Make a new, blank isotope scheme' ] texts = ['Copy', 'Delete', 'New'] commands = [self.copyScheme, self.removeScheme, self.makeNewScheme] self.schemeButtons = ButtonList(frameA, texts=texts, commands=commands, grid=(1, 0), tipTexts=tipTexts) # # Isotopomers # frameB.expandGrid(3, 0) row = 0 frame = Frame(frameB, grid=(row, 0)) frame.expandGrid(0, 2) tipText = 'Selects which of the available isotope schemes to view/edit' label = Label(frame, text='Reference Scheme:', grid=(0, 0)) self.schemePulldown = PulldownList(frame, callback=self.setLabellingScheme, grid=(0, 1), tipText=tipText) row += 1 div = LabelDivider(frameB, text='Isotopomers', grid=(row, 0)) row += 1 frame = Frame(frameB, grid=(row, 0)) frame.expandGrid(1, 2) self.isotopomerFrame = frame self.abundanceWidget = MultiWidget(self, FloatEntry, relief='raised', borderwidth=2, callback=self.setDefaultAbundances, useImages=False) tipText = 'Opens a panel that allows you to set the basis/default abundances for C, H & N isotopes; used as the starting point for new isotopomer definitions' self.abundanceButton = Button(frame, text='Set Default\nAbundances', borderwidth=1, command=self.enterDefaultAbundances, grid=(0, 0), tipText=tipText) tipText = 'Sets the basis/default abundances for C, H & N isotopes to their natural abundance proportions' button = Button(frame, text='Set Natural\nAbundance Default', borderwidth=1, command=self.resetDefaultAbundance, grid=(0, 1), sticky='ew', tipText=tipText) label = Label(frame, text='Molecule Type:', grid=(0, 2), sticky='e') entries = standardResidueCcpCodes.keys() entries.sort() entries.reverse() tipText = 'Selects which type of bio-polymer to define residue isotopomer labelling for' self.molTypePulldown = PulldownList(frame, callback=self.setMolType, texts=entries, grid=(0, 3), tipText=tipText) row += 1 tipTexts = [ 'The CCPN code that identifies the kind of residue the isotopomer relates to', 'The number of the particular isotopomer (isotope pattern) within its residue type', 'The fraction of the total residues, of its kind, that the isotopomer make up' ] headingList = ['Ccp Code', 'Variant', 'Weight'] self.isotopomerWeightEntry = FloatEntry( self, text='', returnCallback=self.setIsotopomerWeight, width=6) editWidgets = [None, None, self.isotopomerWeightEntry] editGetCallbacks = [None, None, self.getIsotopomerWeight] editSetCallbacks = [None, None, self.setIsotopomerWeight] self.isotopomerMatrix = ScrolledMatrix( frameB, tipTexts=tipTexts, headingList=headingList, callback=self.selectIsotopomer, editWidgets=editWidgets, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, multiSelect=True, grid=(row, 0)) self.isotopomerMatrix.doEditMarkExtraRules = self.isotopomerEditRules row += 1 frame = Frame(frameB, grid=(row, 0), sticky='ew') frame.expandGrid(0, 0) tipTexts = [ 'Delete the selected residue isotopomers from the current isotope scheme', 'Make a new residue isotopomer definition by copying the details of the last selected isotopomer', 'Add a complete set of isotopomers to the isotope scheme, one for each residue type, based on the states default isotope abundances', 'For all residue isotopomers in the scheme, set the labelling of one kind of atom (the user is prompted) to its default isotopic incorporation ', 'Add a new residue isotopomer definition that uses the default isotopic incorporation' ] texts = [ 'Delete\nSelected', 'Copy\nSelected', 'Add Default\nAbundance Set', 'Set Atom Type\nTo Default', 'Add\nNew:' ] commands = [ self.removeIsotopomers, self.duplicateResidues, self.addDefaultIsotopomers, self.setAtomTypeDefault, self.addNewIsotopomer ] self.isotopomerButtons = ButtonList(frame, texts=texts, commands=commands, grid=(0, 0), tipTexts=tipTexts) tipText = 'Selects which kind of residue isotopomer may be added to the current isotope scheme' self.ccpCodePulldown = PulldownList(frame, callback=None, grid=(0, 1), sticky='e', tipText=tipText) row += 1 div = LabelDivider(frameB, text='Atom Labels', grid=(row, 0)) row += 1 frame = Frame(frameB, grid=(row, 0)) frame.expandGrid(1, 3) label = Label(frame, text='Chemical Element:', grid=(0, 0)) tipText = 'Selects which kind of atoms to select from the selected residue isotopomer; to display isotopic incorporation in the below table' self.elementPulldown = PulldownList(frame, callback=self.changeChemElement, grid=(0, 1), tipText=tipText) self.updateChemElements() label = Label(frame, text='Water Exchangeable Atoms:', grid=(0, 2)) tipText = 'Sets whether to show atoms considered as being "water exchangeable"; their isotopic labelling will rapidly equilibrate with aqueous solvent' self.exchangeCheck = CheckButton(frame, callback=self.updateAtomLabelsAfter, grid=(0, 3), selected=False, tipText=tipText) row += 1 # Tip texts set on update headingList = [ 'Atom\nName', 'Weighting\n13C' 'Weighting\n12C', '%12C', '%13C' ] self.atomLabelTupleWeightEntry = FloatEntry( self, text='', width=6, returnCallback=self.setAtomLabelWeight) self.atomsMatrix = ScrolledMatrix(frameB, headingList=headingList, callback=self.selectAtomLabel, multiSelect=True, grid=(row, 0)) self.atomsMatrix.doEditMarkExtraRules = self.atomsEditRules row += 1 tipTexts = [ 'For the selected atom sites, in the current isotopomer, set their isotopic incorporation to the default values', 'Spread the isotopic incorporation values from the last selected atom site to all selected atoms sites' ] texts = ['Reset Selected to Default Abundance', 'Propagate Abundances'] commands = [self.setAtomLabelsDefault, self.propagateAbundances] self.atomButtons = ButtonList(frameB, texts=texts, commands=commands, grid=(row, 0), tipTexts=tipTexts) # # View Frame # frameC.expandGrid(1, 0) row = 0 frame = Frame(frameC, grid=(row, 0), sticky='ew') frame.grid_columnconfigure(3, weight=1) label = Label(frame, text='Residue Type:', grid=(0, 0)) tipText = 'Selects which kind of residue, within the current isotope scheme, to show isotopomer structures for' self.viewCcpCodePulldown = PulldownList( frame, callback=self.selectViewCcpcode, grid=(0, 1), tipText=tipText) label = Label(frame, text='Isotopomer:', grid=(0, 2)) tipText = 'Selects which kind of isotopomer (labelling pattern) to display, from the selected residue type.' self.viewIsotopomerPulldown = PulldownList( frame, callback=self.selectViewIsotopomer, grid=(0, 3), tipText=tipText) row += 1 self.viewIsotopomerFrame = ViewIsotopomerFrame(frameC, None, grid=(row, 0)) # # Main # tipTexts = [ 'Save all changes to the reference isotope scheme to disk; the saves ALL changes to the CCPN installation for all projects to use', ] texts = ['Save Schemes'] commands = [self.saveSchemes] self.bottomButtons = UtilityButtonList(tabbedFrame.sideFrame, texts=texts, commands=commands, helpUrl=self.help_url, grid=(0, 0), sticky='e', tipTexts=tipTexts) self.updateChemElements() self.updateCcpCodes() self.updateSchemes() self.administerNotifiers(self.registerNotify) def atomsEditRules(self, atomLabel, row, col): if self.scheme: return isSchemeEditable(self.scheme) else: return False def isotopomerEditRules(self, isotopomer, row, col): if self.scheme: return isSchemeEditable(self.scheme) else: return False def schemeEditRules(self, scheme, row, col): return isSchemeEditable(scheme) def administerNotifiers(self, notifyFunc): for func in ('__init__', 'delete', 'setLongName', 'setDetails'): for clazz in ('ccp.molecule.ChemCompLabel.LabelingScheme', ): notifyFunc(self.updateSchemes, clazz, func) for func in ('__init__', 'delete', 'setWeight'): notifyFunc(self.updateIsotopomersAfter, 'ccp.molecule.ChemCompLabel.Isotopomer', func) notifyFunc(self.updateAtomLabelsAfter, 'ccp.molecule.ChemCompLabel.AtomLabel', func) def getCcpCodeIsotopomers(self, ccpCode): chemCompLabel = self.scheme.findFirstChemCompLabel( molType=self.molType, ccpCode=ccpCode) if chemCompLabel: isotopomers = list(chemCompLabel.isotopomers) else: isotopomers = [] return isotopomers def selectViewCcpcode(self, ccpCode): if ccpCode != self.ccpCodeV: self.ccpCodeV = ccpCode self.isotopomerV = False self.updateViewIsotopomerPulldown() def selectViewIsotopomer(self, isotopomer): self.isotopomerV = isotopomer if isotopomer is None: isotopomers = self.getCcpCodeIsotopomers(self.ccpCodeV) else: isotopomers = [ isotopomer, ] self.viewIsotopomerFrame.setIsotopomers(isotopomers) def updateViewCcpCodePulldown(self): if self.scheme: codes = self.getCcpCodes(self.molType) if self.ccpCodeV not in codes: self.ccpCodeV = codes[0] self.isotopomerV = False # Not None self.updateViewIsotopomerPulldown() index = codes.index(self.ccpCodeV) else: codes = [] index = 0 self.viewCcpCodePulldown.setup(codes, codes, index) def updateViewIsotopomerPulldown(self): index = 0 isotopomers = [] names = [] if self.scheme: isotopomers = self.getCcpCodeIsotopomers(self.ccpCodeV) names = ['%d' % i.serial for i in isotopomers] isotopomers.insert(0, None) names.insert(0, '<All>') if self.isotopomerV not in isotopomers: self.isotopomerV = None isotopomers = self.getCcpCodeIsotopomers(self.ccpCodeV) self.viewIsotopomerFrame.setIsotopomers(isotopomers) self.viewIsotopomerPulldown.setup(names, isotopomers, index) def updateButtons(self): buttonsA = self.schemeButtons.buttons buttonsB = self.isotopomerButtons.buttons buttonsC = self.atomButtons.buttons if self.scheme: buttonsA[0].enable() isEditable = isSchemeEditable(self.scheme) if isEditable: buttonsA[1].enable() else: buttonsA[1].disable() buttonsB[2].enable() buttonsB[3].enable() self.bottomButtons.buttons[0].enable() if isEditable: if self.isotopomer: for button in buttonsB: button.enable() for button in buttonsC: button.enable() else: buttonsB[0].disable() buttonsB[1].disable() buttonsB[3].disable() buttonsC[0].disable() buttonsC[1].disable() buttonsB[2].enable() buttonsB[4].enable() else: for button in buttonsB: button.disable() for button in buttonsC: button.disable() else: buttonsA[0].disable() buttonsA[1].disable() for button in buttonsB: button.disable() for button in buttonsC: button.disable() self.bottomButtons.buttons[0].disable() def resetDefaultAbundance(self): self.defaultAbun = {} def setDefaultAbundances(self, values): self.abundanceWidget.place_forget() if values is not None: i = 0 for element in elementSymbols: # TBD getAllIsotopes for code, isotope in getSortedIsotopes(self.project, element): self.defaultAbun[isotope] = values[i] i += 1 def enterDefaultAbundances(self): x = self.isotopomerFrame.winfo_x() y = self.isotopomerFrame.winfo_y() x0 = self.abundanceButton.winfo_x() y0 = self.abundanceButton.winfo_y() values = [] options = [] for element in elementSymbols: # TBD getAllIsotopes for code, isotope in getSortedIsotopes(self.project, element): options.append(code + ':') values.append( self.defaultAbun.get(isotope, 100.0 * isotope.abundance)) N = len(values) self.abundanceWidget.maxRows = N self.abundanceWidget.minRows = N self.abundanceWidget.set(values=values, options=options) self.abundanceWidget.place(x=x + x0, y=y + y0) def selectAtomLabel(self, obj, row, col): self.atomLabelTuple = (obj, col) def setMolType(self, molType): if molType != self.molType: self.molType = molType self.isotopomer = None self.updateCcpCodes() self.updateIsotopomers() def getCcpCodes(self, molType): codes = [] for code in standardResidueCcpCodes[molType]: codes.append(code) codes.sort() return codes def updateCcpCodes(self): codes = self.getCcpCodes(self.molType) if self.isotopomer: index = codes.index(self.isotopomer.ccpCode) else: index = 0 self.ccpCodePulldown.setup(codes, codes, index) def setIsotopomerWeight(self, event): value = self.isotopomerWeightEntry.get() if value is not None: self.isotopomer.setWeight(abs(value)) def getIsotopomerWeight(self, isotopomer): if isotopomer: self.isotopomerWeightEntry.set(isotopomer.weight) def setSchemeName(self, event): text = self.schemeNameEntry.get() if text: text = text.strip() or None else: text = None self.scheme.setLongName(text) def getSchemeName(self, scheme): if scheme: self.schemeNameEntry.set(scheme.longName) def getSchemeDetails(self, scheme): if scheme: self.schemeDetailsEntry.set(scheme.details) def setSchemeDetails(self, event): text = self.schemeDetailsEntry.get() if text: text = text.strip() or None else: text = None self.scheme.setDetails(text) def updateSchemes(self, obj=None): textMatrix = [] objectList = [] for labelingScheme in self.project.sortedLabelingSchemes(): repository = labelingScheme.findFirstActiveRepository() if repository: saveLocation = repository.name else: saveLocation = None line = [ labelingScheme.name, labelingScheme.longName, labelingScheme.details, saveLocation ] textMatrix.append(line) objectList.append(labelingScheme) self.schemeMatrix.update(textMatrix=textMatrix, objectList=objectList) self.updateSchemePulldown() self.updateIsotopomers() def updateSchemePulldown(self): scheme = self.scheme schemes = self.project.sortedLabelingSchemes() names = [ls.longName or ls.name for ls in schemes] if names: if scheme not in schemes: scheme = schemes[0] index = schemes.index(scheme) else: index = 0 scheme = None self.setLabellingScheme(scheme) self.schemePulldown.setup(names, schemes, index) def copyScheme(self): if self.scheme: name = askString('Input text', 'New Scheme Code:', '', parent=self) scheme = self.project.findFirstLabelingScheme(name=name) if scheme: showWarning('Failure', 'Scheme name already in use') return if name: newScheme = copySubTree(self.scheme, self.project, topObjectParameters={'name': name}) else: showWarning('Failure', 'No name specified') else: showWarning('Failure', 'No scheme selected to copy') def removeScheme(self): if self.scheme and isSchemeEditable(self.scheme): self.scheme.delete() self.scheme = None def makeNewScheme(self): name = askString('Input text', 'New Scheme Code:', '', parent=self) if name: scheme = self.project.findFirstLabelingScheme(name=name) if scheme: showWarning('Failure', 'Scheme name already in use') else: scheme = self.project.newLabelingScheme(name=name) self.scheme = scheme else: showWarning('Failure', 'No name specified') def setLabellingScheme(self, scheme): if scheme is not self.scheme: self.scheme = scheme self.isotopomerV = False self.isotopomer = None self.updateIsotopomers() def selectScheme(self, object, row, col): self.setLabellingScheme(object) self.updateSchemePulldown() def open(self): BasePopup.open(self) self.updateSchemes() def saveSchemes(self): schemes = [x for x in self.project.labelingSchemes if x.isModified] if schemes: for scheme in schemes: scheme.save() showInfo('Notice', 'Successfully saved %d schemes' % len(schemes)) self.updateSchemes() else: showWarning('Notice', 'No modified schemes to save') def addNewIsotopomer(self): if self.scheme: ccpCode = self.ccpCodePulldown.getObject() chemCompLabel = self.getChemCompLabel(self.molType, ccpCode) self.makeIsotopomer(chemCompLabel) def makeIsotopomer(self, chemCompLabel, weight=1.0): isotopomer = chemCompLabel.newIsotopomer(weight=weight) chemComp = chemCompLabel.chemComp for chemAtom in chemComp.chemAtoms: if chemAtom.elementSymbol: chemElement = chemAtom.chemElement for isotope in chemElement.isotopes: code = '%d%s' % (isotope.massNumber, chemAtom.elementSymbol) weight = self.defaultAbun.get(isotope, 100.0 * isotope.abundance) isotopomer.newAtomLabel(name=chemAtom.name, subType=chemAtom.subType, isotopeCode=code, weight=weight) return isotopomer def getChemCompLabel(self, molType, ccpCode): chemCompLabel = None if self.scheme: chemCompLabel = self.scheme.findFirstChemCompLabel(molType=molType, ccpCode=ccpCode) if not chemCompLabel: chemCompLabel = self.scheme.newChemCompLabel(molType=molType, ccpCode=ccpCode) return chemCompLabel def selectIsotopomer(self, obj, row, col): self.isotopomer = obj self.updateChemElements() self.updateAtomLabels() def updateIsotopomersAfter(self, obj=None): if self.waiting: return else: self.waiting = True self.after_idle(self.updateIsotopomers) def updateIsotopomers(self): self.updateViewCcpCodePulldown() self.updateViewIsotopomerPulldown() textMatrix = [] objectList = [] if self.scheme: chemCompLabels = [(label.ccpCode, label) for label in self.scheme.chemCompLabels] chemCompLabels.sort() for key, chemCompLabel in chemCompLabels: if chemCompLabel.molType == self.molType: for isotopomer in chemCompLabel.sortedIsotopomers(): line = [ chemCompLabel.ccpCode, isotopomer.serial, isotopomer.weight ] textMatrix.append(line) objectList.append(isotopomer) self.isotopomerMatrix.update(textMatrix=textMatrix, objectList=objectList) self.updateAtomLabelsAfter() self.waiting = False def setAtomTypeDefault(self): if self.scheme: atomName = askString( 'Query', 'Specify atom name to set\ndefault abundance for', 'H', parent=self) if not atomName: return atomLabels = [] for chemCompLabel in self.scheme.chemCompLabels: if chemCompLabel.molType == self.molType: for isotopomer in chemCompLabel.isotopomers: # Multiple because of isotopes and subTypes atomLabels += isotopomer.findAllAtomLabels( name=atomName) if atomLabels: for atomLabel in atomLabels: isotope = atomLabel.isotope weight = self.defaultAbun.get(isotope, 100.0 * isotope.abundance) atomLabel.weight = weight else: data = (atomName, self.scheme.name) msg = 'Atom name %s does not match any atoms in %s scheme isotopomers' % data showWarning('Failure', msg) def addDefaultIsotopomers(self): if self.scheme: codes = self.getCcpCodes(self.molType) for ccpCode in codes: chemCompLabel = self.getChemCompLabel(self.molType, ccpCode) if not chemCompLabel.isotopomers: self.makeIsotopomer(chemCompLabel) def removeIsotopomers(self): isotopomers = self.isotopomerMatrix.currentObjects if isotopomers: for isotopomer in isotopomers: isotopomer.delete() self.isotopomer = None def duplicateResidues(self): isotopomers = self.isotopomerMatrix.currentObjects for isotopomer in isotopomers: chemCompLabel = isotopomer.chemCompLabel new = copySubTree(isotopomer, chemCompLabel) def updateChemElements(self): if self.isotopomer: chemCompLabel = self.isotopomer.chemCompLabel elementDict = {} for chemAtom in chemCompLabel.chemComp.chemAtoms: symbol = chemAtom.elementSymbol if symbol: elementDict[symbol] = True names = elementDict.keys() names.sort() else: names = ['C', 'N', 'H'] if self.element not in names: index = 0 else: index = names.index(self.element) self.elementPulldown.setup(names, names, index) def updateAtomLabelsAfter(self, obj=None): if self.waitingAtom: return else: self.waitingAtom = True self.after_idle(self.updateAtomLabels) def updateAtomLabels(self): element = self.elementPulldown.getText() textMatrix = [] objectList = [] headingList = [ 'Atom Name', ] tipTexts = [ 'The name of the atom within its residue, for which to set isotopic abundances, within the selected isotopomer', ] isotopeCodes = [x[0] for x in getSortedIsotopes(self.project, element)] headingList.extend(['Weighting\n%s' % x for x in isotopeCodes]) tip = 'The amount of %s isotope incorporation; can be a ratio, percentage or fraction (the value used is relative to the sum of all weights)' tipTexts.extend([tip % x for x in isotopeCodes]) tip = 'The percentage %s isotope incorporation, calculated using stated weights' headingList.extend(['%%%s' % x for x in isotopeCodes]) tipTexts.extend([tip % x for x in isotopeCodes]) editWidgets = [ None, ] editGetCallbacks = [ None, ] editSetCallbacks = [ None, ] editWidgets.extend( [self.atomLabelTupleWeightEntry for x in isotopeCodes]) editGetCallbacks.extend( [self.getAtomLabelWeight for x in isotopeCodes]) editSetCallbacks.extend( [self.setAtomLabelWeight for x in isotopeCodes]) editWidgets.extend([None for x in isotopeCodes]) editGetCallbacks.extend([None for x in isotopeCodes]) editSetCallbacks.extend([None for x in isotopeCodes]) doExchangeable = self.exchangeCheck.get() if self.isotopomer: atomDict = {} for atomLabel in self.isotopomer.sortedAtomLabels(): if atomLabel.chemAtom.elementSymbol == element: if (not doExchangeable) and ( atomLabel.chemAtom.waterExchangeable): continue name = atomLabel.name subType = atomLabel.subType atomDict[(name, subType)] = True atomNames = atomDict.keys() atomNames = greekSortAtomNames(atomNames) for atomName, subType in atomNames: if subType == 1: name = atomName else: name = '%s:%d' % (atomName, subType) line = [ name, ] atomLabels = [] sumWeights = 0.0 for isotope in isotopeCodes: atomLabel = self.isotopomer.findFirstAtomLabel( name=atomName, subType=subType, isotopeCode=isotope) atomLabels.append(atomLabel) if atomLabel: weight = atomLabel.weight sumWeights += weight line.append(weight) else: line.append(0.0) if sumWeights: for atomLabel in atomLabels: line.append(100.0 * atomLabel.weight / sumWeights) else: for atomLabel in atomLabels: line.append(None) textMatrix.append(line) objectList.append(atomLabels) self.atomsMatrix.update(textMatrix=textMatrix, objectList=objectList, headingList=headingList, tipTexts=tipTexts, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks) self.waitingAtom = False self.updateButtons() def setAtomLabelWeight(self, event): value = self.atomLabelTupleWeightEntry.get() if value is not None: atomLabels, col = self.atomLabelTuple chemAtom = None for label in atomLabels: if label: chemAtom = label.chemAtom break atomLabel = atomLabels[col - 1] if chemAtom and (atomLabel is None): isotopeCode, isotope = getSortedIsotopes( self.project, chemAtom.elementSymbol)[col - 1] atomLabel = self.isotopomer.newAtomLabel( name=chemAtom.name, subType=chemAtom.subType, isotopeCode=isotopeCode, weight=value) atomLabel.setWeight(value) def setAtomLabelsDefault(self): atomLabelTuples = self.atomsMatrix.currentObjects for atomLabels in atomLabelTuples: for atomLabel in atomLabels: isotope = atomLabel.isotope weight = self.defaultAbun.get(isotope, 100.0 * isotope.abundance) atomLabel.weight = weight def propagateAbundances(self): atomLabels, col = self.atomLabelTuple atomLabelTuples = self.atomsMatrix.currentObjects weightDict = {} for atomLabel in atomLabels: weightDict[atomLabel.isotope] = atomLabel.weight for atomLabels0 in atomLabelTuples: if atomLabels0 != atomLabels: for atomLabel in atomLabels0: atomLabel.weight = weightDict[atomLabel.isotope] def getAtomLabelWeight(self, null): atomLabels, col = self.atomLabelTuple if atomLabels and (col > 0): atomLabel = atomLabels[col - 1] if atomLabel is None: weight = 0.0 else: weight = atomLabel.weight self.atomLabelTupleWeightEntry.set(weight) def changeChemElement(self, name): self.element = name self.isotopes = [x[0] for x in getSortedIsotopes(self.project, name)] self.updateAtomLabels() def destroy(self): self.administerNotifiers(self.unregisterNotify) BasePopup.destroy(self)
class BrukerPseudoPopup(BasePopup): def __init__(self, parent, params, dim, *args, **kw): self.dim = dim self.params = params BasePopup.__init__(self, parent=parent, title='Bruker Pseudo Data', modal=True, **kw) def body(self, master): pseudoExpts = getSampledDimExperiments(self.parent.nmrProject) master.rowconfigure(0, weight=1) master.rowconfigure(1, weight=1) master.columnconfigure(0, weight=1) tipTexts = ['The experiment is pseudo-N dimensional, with a sampled axis', 'The experiment is the regular kind with only NMR frequency axes'] self.pseudoEntries = [x % len(self.params.npts) for x in PSEUDO_ENTRIES] self.pseudoButton = RadioButtons(master, entries=self.pseudoEntries, select_callback=self.changedPseudoMode, grid=(0,0), sticky='nw', tipTexts=tipTexts) frame = self.pseudoFrame = Frame(master) self.pseudoFrame.grid(row=1, column=0, sticky='nsew') row = 0 if pseudoExpts: tipText = 'Select from existing pseudo nD experiments to copy sampled axis values from' texts = [x.name for x in pseudoExpts] label = Label(frame, text='Existing pseudo expts: ') label.grid(row=row, column=0, sticky='e') self.pseudoList = PulldownList(frame, texts=texts, objects=pseudoExpts, tipText=tipText) self.pseudoList.grid(row=row, column=1, sticky='w') tipText = 'Transfer the sampled axis values from the existing experiment to the new one' Button(frame, text='Copy values down', command=self.copyValues, tipText=tipText, grid=(row,2)) row += 1 npts = self.params.npts[self.dim] tipText = 'Number of data points (planes) along sampled axis' label = Label(frame, text='Number of points: ') label.grid(row=row, column=0, sticky='e') self.nptsEntry = IntEntry(frame, text=npts, tipText=tipText, width=8, grid=(row,1)) tipText = 'Load the values for the sampled axis from a text file containing a list of numeric values' Button(frame, text='Load File', command=self.loadValues, tipText=tipText, grid=(row,2), sticky='ew') row += 1 tipText = 'The values (e.g. T1, T2) corresponding to each data point (plane) along sampled axis' label = Label(frame, text='Point values: ') label.grid(row=row, column=0, sticky='e') self.valueEntry = FloatEntry(frame, isArray=True, tipText=tipText) #minRows = self.params.npts[self.dim] #self.valueEntry = MultiWidget(frame, FloatEntry, callback=None, minRows=minRows, maxRows=None, # options=None, values=[], useImages=False) self.valueEntry.grid(row=row, column=1, columnspan=2, sticky='ew') row += 1 label = Label(frame, text='(requires comma-separated list, of length number of points)') label.grid(row=row, column=1, columnspan=2, sticky='w') row += 1 for n in range(row): frame.rowconfigure(n, weight=1) frame.columnconfigure(1, weight=1) buttons = UtilityButtonList(master, closeText='Ok', closeCmd=self.updateParams, helpUrl=self.help_url) buttons.grid(row=row, column=0, sticky='ew') def loadValues(self): directory = self.parent.fileSelect.getDirectory() fileSelectPopup = FileSelectPopup(self, title='Select Sampled Data File', dismiss_text='Cancel', selected_file_must_exist=True, multiSelect=False, directory=directory) fileName = fileSelectPopup.file_select.getFile() fileObj = open(fileName, 'rU') data = '' line = fileObj.readline() while line: data += line line = fileObj.readline() data = re.sub(',\s+', ',', data) data = re.sub('\s+', ',', data) data = re.sub(',,', ',', data) data = re.sub('[^0-9,.\-+eE]', '', data) self.valueEntry.set(data) def copyValues(self): expt = self.pseudoList.getObject() if expt: dataDim = getExperimentSampledDim(expt) values = dataDim.pointValues self.nptsEntry.set(len(values)) self.valueEntry.set(values) def updateParams(self): params = self.params if self.pseudoButton.get() == self.pseudoEntries[0]: npts = self.nptsEntry.get() params.npts[self.dim] = npts values = self.valueEntry.get() try: params.setSampledDim(self.dim, values) except ApiError, e: showError('Set Sampled Dim', e.error_msg, parent=self) return else:
class AnnealingSettingsTab(object): '''This class describes the tab in the GUI where the user can change setting that govern the monte carlo / annleaing procedure. This also includes which information from the ccpn analysis project is used and which information is ignored. This includes: * present sequential assignments * tentative assignments * amino acid type information * whether to include untyped spin systems * assignments to peak dimensions ALso the chain can be selected here. Furthermore the user can set the temperature regime of the annealing, the amount of times the procedure is repeated to obtain statistics. The fraction of peaks that is left out in each run to diversify the results, the treshhold score for amino acid typing and the treshhold collabelling for a peak to be expected. ''' def __init__(self, parent, frame): '''Init. args: parent: the guiElement that this tab is part of. frame: the frame this part of the GUI lives in. ''' self.guiParent = parent self.frame = frame self.project = parent.project self.nmrProject = parent.nmrProject self.minIsoFrac = 0.1 self.leavePeaksOutFraction = 0.0 self.minTypeScore = 1.0 self.chain = None self.amountOfRepeats = 10 self.amountOfSteps = 10000 self.acceptanceConstantList = [0.0, 0.01, 0.015, 0.022, 0.033, 0.050, 0.075, 0.113, 0.170, 0.256, 0.384, 0.576, 0.864, 1.297, 1.946, 2.919, 4.378, 6.568, 9.852, 14.77, 22.16, 33.25] self.energyDataSets = [[]] self.residues = [] self.body() def body(self): '''describes the body of this tab. It bascically consists of some field to fill out for the user at the top and a ScrolledGraph that shows the progess of the annealing procedure a the bottom. ''' frame = self.frame # frame.expandGrid(13,0) frame.expandGrid(15, 1) row = 0 text = 'Calculate Assignment Suggestions' command = self.runCalculations self.startButton = Button(frame, command=command, text=text) self.startButton.grid(row=row, column=0, sticky='nsew', columnspan=2) row += 1 Label(frame, text='Amount of runs: ', grid=(row, 0)) tipText = 'The amount of times the whole optimization procedure is performed, each result is safed' self.repeatEntry = IntEntry(frame, grid=(row, 1), width=7, text=10, returnCallback=self.updateRepeatEntry, tipText=tipText, sticky='nsew') self.repeatEntry.bind('<Leave>', self.updateRepeatEntry, '+') row += 1 Label(frame, text='Temperature regime: ', grid=(row, 0)) tipText = 'This list of numbers govern the temperature steps during the annealing, every number represents 1/(kb*t), where kb is the Boltzmann constant and t the temperature of one step.' self.tempEntry = Entry(frame, text=map(str, self.acceptanceConstantList), width=64, grid=(row, 1), isArray=True, returnCallback=self.updateAcceptanceConstantList, tipText=tipText, sticky='nsew') row += 1 Label(frame, text='Amount of attempts per temperature:', grid=(row, 0)) tipText = 'The amount of attempts to switch the position of two spinsystems in the sequence are performed for each temperature point' self.NAStepEntry = IntEntry(frame, grid=(row, 1), width=7, text=10000, returnCallback=self.updateStepEntry, tipText=tipText, sticky='nsew') self.NAStepEntry.bind('<Leave>', self.updateStepEntry, '+') row += 1 Label(frame, text='Fraction of peaks to leave out:', grid=(row, 0)) tipText = 'In each run a fraction of the peaks can be left out of the optimization, thereby increasing the variability in the outcome and reducing false negatives. In each run this will be different randomly chosen sub-set of all peaks. 0.1 (10%) can be a good value.' self.leaveOutPeaksEntry = FloatEntry(frame, grid=(row, 1), width=7, text=0.0, returnCallback=self.updateLeavePeaksOutEntry, tipText=tipText, sticky='nsew') self.leaveOutPeaksEntry.bind( '<Leave>', self.updateLeavePeaksOutEntry, '+') row += 1 Label(frame, text='Minmal amino acid typing score:', grid=(row, 0)) tipText = 'If automatic amino acid typing is selected, a cut-off value has to set. Every amino acid type that scores higher than the cut-off is taken as a possible type. This is the same score as can be found under resonance --> spin systems --> predict type. Value should be between 0 and 100' self.minTypeScoreEntry = FloatEntry(frame, grid=(row, 1), width=7, text=1.0, returnCallback=self.updateMinTypeScoreEntry, tipText=tipText, sticky='nsew') self.minTypeScoreEntry.bind( '<Leave>', self.updateMinTypeScoreEntry, '+') row += 1 Label(frame, text='Minimal colabelling fraction:', grid=(row, 0)) tipText = 'The minimal amount of colabelling the different nuclei should have in order to still give rise to a peak.' self.minLabelEntry = FloatEntry(frame, grid=(row, 1), width=7, text=0.1, returnCallback=self.updateMinLabelEntry, tipText=tipText, sticky='nsew') self.minLabelEntry.bind('<Leave>', self.updateMinLabelEntry, '+') row += 1 Label(frame, text='Use sequential assignments:', grid=(row, 0)) tipText = 'When this option is select the present sequential assignments will be kept in place' self.useAssignmentsCheck = CheckButton( frame, selected=True, tipText=tipText, grid=(row, 1)) row += 1 Label(frame, text='Use tentative assignments:', grid=(row, 0)) tipText = 'If a spin system has tentative assignments this can be used to narrow down the amount of possible sequential assignments.' self.useTentativeCheck = CheckButton( frame, selected=True, tipText=tipText, grid=(row, 1)) row += 1 Label(frame, text='Use amino acid types:', grid=(row, 0)) tipText = 'Use amino acid types of the spin systems. If this option is not checked the spin systems are re-typed, only resonance names and frequencies are used' self.useTypeCheck = CheckButton( frame, selected=True, tipText=tipText, grid=(row, 1)) row += 1 Label(frame, text='Include untyped spin systems:', grid=(row, 0)) tipText = 'Also include spin system that have no type information. Amino acid typing will be done on the fly.' self.useAlsoUntypedSpinSystemsCheck = CheckButton( frame, selected=True, tipText=tipText, grid=(row, 1)) row += 1 Label(frame, text='Use dimensional assignments:', grid=(row, 0)) tipText = 'If one or more dimensions of a peak is already assigned, assume that this assignment is the only option. If not the check the program will consider all possibilities for the assignment of the dimension.' self.useDimensionalAssignmentsCheck = CheckButton( frame, selected=True, tipText=tipText, grid=(row, 1)) row += 1 Label(frame, text='Chain:', grid=(row, 0)) self.molPulldown = PulldownList( frame, callback=self.changeMolecule, grid=(row, 1)) self.updateChains() row += 1 Label(frame, text='Residue ranges: ', grid=(row, 0)) tipText = 'Which residues should be included. Example: "10-35, 62-100, 130".' self.residueRangeEntry = Entry(frame, text=None, width=64, grid=(row, 1), isArray=True, returnCallback=self.updateResidueRanges, tipText=tipText, sticky='nsew') self.updateResidueRanges(fromChain=True) row += 1 self.energyPlot = ScrolledGraph(frame, symbolSize=2, width=600, height=200, title='Annealing', xLabel='temperature step', yLabel='energy') self.energyPlot.grid(row=row, column=0, columnspan=2, sticky='nsew') def runCalculations(self): '''Run all calculations. Also triggers the disabling of some buttons and fields. ''' self.startButton.disable() self.disableIllegalButtonsAfterPrecalculations() self.guiParent.connector.runAllCalculations() self.startButton.configure(text='More runs') self.startButton.enable() def disableIllegalButtonsAfterPrecalculations(self): '''Disable buttons and field the user can not alter any longer after the model is set up and the 'pre-calculations' have finished. This is done because this part of the calculation should only be run once. All settings that would be changed after this point will not have any influence. ''' illegalButtons = [self.minTypeScoreEntry, self.minLabelEntry, self.useAlsoUntypedSpinSystemsCheck, self.useAssignmentsCheck, self.useTypeCheck, self.useDimensionalAssignmentsCheck, self.useTentativeCheck] for illegalButton in illegalButtons: illegalButton.configure(state='disabled') self.molPulldown.disable() def getChainName(self, chain): '''Get the name for a chain. args: chain: ccpn analysis chain object returns: chain name ''' return '%s:%s (%s)' % (chain.molSystem.code, chain.code, chain.molecule.molType) def getChains(self): '''Get all chains present in the project. returns: list of ccpn analysis chain objects ''' chains = [] if self.project: for molSystem in self.project.sortedMolSystems(): for chain in molSystem.sortedChains(): if chain.residues: chains.append(chain) return chains def updateChains(self, *opt): '''Updates the list of chains if a new one is added to or deleted from the project. Updates the pull down list where a chain can be selected. ''' index = 0 texts = [] chains = self.getChains() chain = self.chain if chains: if chain not in chains: chain = chains[0] texts = [self.getChainName(c) for c in chains] index = chains.index(chain) else: chain = None self.molPulldown.setup(texts, chains, index) if chain is not self.chain: self.chain = chain def changeMolecule(self, chain): '''Select a molecular chain.''' if chain is not self.chain: self.chain = chain self.updateResidueRanges(fromChain=True) def updateStepEntry(self, event=None): '''Update the value and entry that sets the amount of steps per temperature point. ''' value = self.NAStepEntry.get() if value == self.amountOfSteps: return if value < 1: self.NAStepEntry.set(1) self.amountOfSteps = 1 else: self.amountOfSteps = value self.NAStepEntry.set(value) def updateRepeatEntry(self, event=None): '''Update the value and entry of that sets the amount of times the whole annealing procedure is repeated in order to obtain statistics. ''' value = self.repeatEntry.get() if value == self.amountOfRepeats: return if value < 1: self.repeatEntry.set(1) self.amountOfRepeats = 1 else: self.amountOfRepeats = value self.repeatEntry.set(value) def updateMinTypeScoreEntry(self, event=None): '''Updates the value and the entry for the treshhold value for amino acid typing. ''' value = self.minTypeScoreEntry.get() if value == self.minTypeScore: return if value < 0: self.minTypeScoreEntry.set(0.0) self.minTypeScore = 0.0 elif value > 100: self.minTypeScoreEntry.set(100.0) self.minTypeScore = 100.0 else: self.minTypeScoreEntry.set(value) self.minTypeScore = value def updateMinLabelEntry(self, event=None): '''Updates the minimum colabelling fraction for which a peak is expected to be present in the spectra. ''' value = self.minLabelEntry.get() if value == self.minIsoFrac: return if value < 0: self.minIsoFrac = 0.0 self.minLabelEntry.set(0.0) elif value > 1: self.minIsoFrac = 1.0 self.minLabelEntry.set(1.0) else: self.minIsoFrac = value self.minLabelEntry.set(value) def updateLeavePeaksOutEntry(self, event=None): '''Updates the value and entry of the fraction of peaks that should be left out in each run in order to diversify the results. ''' value = self.leaveOutPeaksEntry.get() if value == self.leavePeaksOutFraction: return if value < 0: self.leavePeaksOutFraction = 0.0 self.leaveOutPeaksEntry.set(0.0) elif value > 1: self.leavePeaksOutFraction = 1.0 self.leaveOutPeaksEntry.set(1.0) else: self.leavePeaksOutFraction = value self.leaveOutPeaksEntry.set(value) def updateAcceptanceConstantList(self, event=None): '''Updates the list with constants that are used during the monte carlo procedure to decide whether a changed is accepted or not. ''' acList = self.tempEntry.get() newList = [] for constant in acList: try: number = float(constant) newList.append(number) except ValueError: string = constant + \ ' in temperature constants is not a number.' showWarning('Not A Number', string, parent=self.guiParent) return False self.acceptanceConstantList = newList return True def updateResidueRanges(self, event=None, fromChain=False): self.residues = set() subRanges = self.residueRangeEntry.get() if not subRanges or fromChain: self.residues = set(self.chain.residues) residues = self.chain.sortedResidues() text = '{}-{}'.format(residues[0].seqCode, residues[-1].seqCode) self.residueRangeEntry.set(text=text) return for subRange in subRanges: indeces = subRange.split('-') start = int(indeces[0]) stop = int(indeces[-1]) + 1 for seqCode in range(start, stop): residue = self.chain.findFirstResidue(seqCode=seqCode) if not residue: showWarning('Residue out of range.', 'There is no residue at position {}'.format(seqCode), parent=self.guiParent) self.residues = set() return self.residues.add(residue) def addEnergyPoint(self, energy, time): '''Adds a point to the graph that shows the progress of the annealling procedure. args: energy: the y-value time: the x-value ''' point = (time, energy * -1) # This means one run has finished if len(self.energyDataSets[-1]) / (len(self.acceptanceConstantList) + 1): self.energyDataSets.append([point]) else: self.energyDataSets[-1].append(point) colors = colorSeries Ncolors = len(colors) NdataSets = len(self.energyDataSets) colorList = (NdataSets / Ncolors) * colors + \ colors[:NdataSets % Ncolors] self.energyPlot.update(dataSets=self.energyDataSets, dataColors=colorList) # Forcing the graph to draw, eventhough calculations # are still running. Only do this with high numbers of # steps, otherwise drawing takes longer than annealling. if self.amountOfSteps >= 100000: self.energyPlot.draw()
class PrintFrame(LabelFrame): def __init__(self, parent, getOption=None, setOption=None, text='Print Options', haveTicks=False, doOutlineBox=True, *args, **kw): self.getOption = getOption self.setOption = setOption self.haveTicks = haveTicks self.doOutlineBox = doOutlineBox LabelFrame.__init__(self, parent=parent, text=text, *args, **kw) self.file_select_popup = None self.getOptionValues() try: size_index = self.paper_types.index(self.paper_type) except: size_index = 0 try: other_unit_index = self.paper_units.index(self.other_unit) except: other_unit_index = 0 try: orientation_index = self.paper_orientations.index(self.orientation) except: orientation_index = 0 try: style_index = self.style_choices.index(self.output_style) except: style_index = 0 try: format_index = self.format_choices.index(self.output_format) except: format_index = 0 if haveTicks: try: tick_location_index = self.tick_locations.index( self.tick_location) except: tick_location_index = 0 self.grid_columnconfigure(2, weight=1) row = 0 label = Label(self, text='File:') label.grid(row=row, column=0, sticky='e') self.file_entry = Entry(self, width=40, text=self.file_name) self.file_entry.grid(row=row, column=1, columnspan=2, sticky='ew') button = Button(self, text='Choose File', command=self.findFile) button.grid(row=row, column=3, rowspan=2, sticky='nsew') row += 1 label = Label(self, text='Title:') label.grid(row=row, column=0, sticky='e') self.title_entry = Entry(self, width=40, text=self.title) self.title_entry.grid(row=row, column=1, columnspan=2, sticky='ew') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(4, weight=1) label = Label(frame, text='Paper size:') label.grid(row=0, column=0, sticky='e') entries = [] for t in paper_types: if t == Output.other_paper_type: entry = t else: (w, h, u) = paper_sizes[t] entry = t + ' (%2.1f %s x %2.1f %s)' % (w, u, h, u) entries.append(entry) self.size_menu = PulldownList(frame, callback=self.changedSize, texts=entries, index=size_index) self.size_menu.grid(row=0, column=1, sticky='w') self.other_frame = Frame(frame) self.other_frame.grid_columnconfigure(0, weight=1) self.other_entry = FloatEntry(self.other_frame, text=self.other_size, isArray=True) self.other_entry.grid(row=0, column=0, sticky='ew') self.other_unit_menu = PulldownList(self.other_frame, texts=paper_units, index=other_unit_index) self.other_unit_menu.grid(row=0, column=1, sticky='ew') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) frame.grid_columnconfigure(3, weight=1) frame.grid_columnconfigure(5, weight=1) label = Label(frame, text='Orientation:') label.grid(row=0, column=0, sticky='e') self.orientation_menu = PulldownList(frame, texts=paper_orientations, index=orientation_index) self.orientation_menu.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Style:') label.grid(row=0, column=2, sticky='e') self.style_menu = PulldownList(frame, texts=style_choices, index=style_index) self.style_menu.grid(row=0, column=3, sticky='w') label = Label(frame, text=' Format:') label.grid(row=0, column=4, sticky='e') self.format_menu = PulldownList(frame, callback=self.changedFormat, texts=format_choices, index=format_index) self.format_menu.grid(row=0, column=5, sticky='w') if haveTicks: row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) frame.grid_columnconfigure(3, weight=1) label = Label(frame, text='Tick Location:') label.grid(row=0, column=0, sticky='e') self.tick_menu = PulldownList(frame, texts=tick_locations, index=tick_location_index) self.tick_menu.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Tick Placement:') label.grid(row=0, column=2, sticky='e') self.tick_buttons = CheckButtons(frame, entries=tick_placements, selected=self.tick_placement) self.tick_buttons.grid(row=0, column=3, sticky='w') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(3, weight=1) label = Label(frame, text='Include:') label.grid(row=0, column=0, sticky='e') self.border_buttons = CheckButtons(frame, entries=border_decorations, selected=self.border_decoration) self.border_buttons.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Scaling:') label.grid(row=0, column=2, sticky='e') self.scaling_scale = Scale(frame, orient=Tkinter.HORIZONTAL, value=self.scaling) self.scaling_scale.grid(row=0, column=3, sticky='ew') def destroy(self): self.setOptionValues() if self.file_select_popup: self.file_select_popup.destroy() Frame.destroy(self) def getOptionValues(self): getOption = self.getOption if getOption: file_name = getOption('FileName', defaultValue='') title = getOption('Title', defaultValue='') paper_type = getOption('PaperSize', defaultValue=paper_types[0]) paper_type = paper_type_dict.get(paper_type, paper_types[0]) other_height = getOption('OtherHeight', defaultValue=10) other_width = getOption('OtherWidth', defaultValue=10) other_size = [other_height, other_width] other_unit = getOption('OtherUnit', defaultValue=paper_units[0]) orientation = getOption('Orientation', defaultValue=paper_orientations[0]) in_color = getOption('InColor', defaultValue=True) if in_color: output_style = style_choices[0] else: output_style = style_choices[1] format_option = getOption('OutputFormat', defaultValue=format_options[0]) output_format = format_choices[format_options.index(format_option)] if self.haveTicks: tick_outside = getOption('TickOutside', defaultValue=tick_locations[0]) if tick_outside: tick_location = tick_locations.index(PrintTicks.Outside) else: tick_location = tick_locations.index(PrintTicks.Inside) tick_placement = getTickPlacement1( getOption('TickPlacement', defaultValue='nsew')) dateTime = getOption('ShowsDateTime', defaultValue=True) fileName = getOption('ShowsFileName', defaultValue=True) border_decoration = [] if dateTime: border_decoration.append(border_decorations[0]) if fileName: border_decoration.append(border_decorations[1]) scaling = getOption('Scaling', defaultValue=0.9) scaling = int(round(100.0 * scaling)) else: file_name = '' title = '' paper_type = paper_types[0] other_unit = paper_units[0] other_size = '' orientation = paper_orientations[0] output_style = style_choices[0] output_format = format_choices[0] if self.haveTicks: tick_location = tick_locations[0] tick_placement = tick_placements border_decoration = border_decorations scaling = 90 if not self.haveTicks: tick_location = None tick_placement = None self.file_name = file_name self.title = title self.paper_type = paper_type self.other_unit = other_unit self.other_size = other_size self.orientation = orientation self.output_style = output_style self.output_format = output_format self.tick_location = tick_location self.tick_placement = tick_placement self.border_decoration = border_decoration self.scaling = scaling def setOptionValues(self): self.file_name = file_name = self.file_entry.get() self.title = title = self.title_entry.get() n = self.size_menu.getSelectedIndex() self.paper_type = paper_type = paper_types[n] if paper_type == Output.other_paper_type: other_size = self.other_entry.get() other_unit = self.other_unit_menu.getText() else: other_size = None other_unit = None self.other_size = other_size self.other_unit = other_unit self.paper_orientation = paper_orientation = self.orientation_menu.getText( ) self.output_style = output_style = self.style_menu.getText() self.output_format = output_format = self.format_menu.getText() if self.haveTicks: tick_location = self.tick_menu.getText() tick_placement = self.tick_buttons.getSelected() else: tick_location = tick_placement = None self.tick_location = tick_location self.tick_placement = tick_placement self.border_decoration = border_decoration = self.border_buttons.getSelected( ) scaling = self.scaling_scale.get() self.scaling = scaling = int(round(scaling)) setOption = self.setOption if setOption: setOption('FileName', value=file_name) setOption('Title', value=title) if paper_type == Output.other_paper_type: setOption('OtherHeight', value=other_size[0]) setOption('OtherWidth', value=other_size[1]) setOption('OtherUnit', value=other_unit) else: paper_type = paper_type_inverse_dict[paper_type] setOption('PaperSize', value=paper_type) setOption('Orientation', value=paper_orientation) in_color = (output_style == style_choices[0]) setOption('InColor', value=in_color) output_format = format_options[format_choices.index(output_format)] setOption('OutputFormat', value=output_format) if self.haveTicks: tick_outside = (tick_location == PrintTicks.Outside) setOption('TickOutside', value=tick_outside) tick_placement = getTickPlacement2(tick_placement) setOption('TickPlacement', value=tick_placement) dateTime = (border_decorations[0] in border_decoration) fileName = (border_decorations[1] in border_decoration) setOption('ShowsDateTime', value=dateTime) setOption('ShowsFileName', value=fileName) setOption('Scaling', value=0.01 * scaling) def findFile(self): if self.file_select_popup: self.file_select_popup.open() else: file_types = [ FileType('All', ['*']), FileType('PostScript', ['*.ps', '*.eps']), FileType('PDF', ['*.pdf', '*.ai']) ] self.file_select_popup = FileSelectPopup(self, file_types=file_types) file = self.file_select_popup.getFile() if file: self.file_entry.set(file) def changedSize(self, entry): if entry == Output.other_paper_type: self.other_frame.grid(row=0, column=2, columnspan=2, sticky='w') else: self.other_frame.grid_forget() def changedFormat(self, entry): file_suffix = file_suffixes.get(entry) if not file_suffix: return file_name = self.file_entry.get() if not file_name: return for suffix in format_suffixes: if file_name.endswith(suffix): if suffix != file_suffix: n = len(suffix) file_name = file_name[:-n] + file_suffix self.file_entry.set(file_name) break else: file_name = file_name + file_suffix self.file_entry.set(file_name) # width and height are of plot, in pixels def getOutputHandler(self, width, height, fonts=None): if not fonts: fonts = [] else: fonts = list(fonts) for n in range(len(fonts)): if fonts[n] == 'Times': fonts[n] = 'Times-Roman' self.setOptionValues() if not self.file_name: showError('No file', 'No file specified', parent=self) return None if os.path.exists(self.file_name): if not showYesNo('File exists', 'File "%s" exists, overwrite?' % self.file_name, parent=self): return None if (self.paper_type == Output.other_paper_type): paper_size = self.other_size + [self.other_unit] else: paper_size = paper_sizes[self.paper_type] output_scaling = self.scaling / 100.0 font = 'Times-Roman' border_text = {} for decoration in self.border_decoration: if (decoration == 'Time & date'): location = 'se' text = time.ctime(time.time()) elif (decoration == 'File name'): location = 'sw' text = self.file_name else: continue # should not be here border_text[location] = (text, font, 12) if (self.title): location = 'n' border_text[location] = (self.title, font, 18) if font not in fonts: fonts.append(font) outputHandler = PrintHandler.getOutputHandler( self.file_name, width, height, output_scaling=output_scaling, paper_size=paper_size, paper_orientation=self.paper_orientation, output_style=self.output_style, output_format=self.output_format, border_text=border_text, fonts=fonts, do_outline_box=self.doOutlineBox) return outputHandler def getAspectRatio(self): self.setOptionValues() if self.paper_type == Output.other_paper_type: paper_size = self.other_size else: paper_size = paper_sizes[self.paper_type] r = paper_size[1] / paper_size[0] if self.paper_orientation == 'Landscape': r = 1.0 / r return r
class PrintFrame(Frame): def __init__(self, parent, getOption = None, setOption = None, haveTicks = False, doOutlineBox = True, *args, **kw): self.getOption = getOption self.setOption = setOption self.haveTicks = haveTicks self.doOutlineBox = doOutlineBox Frame.__init__(self, parent=parent, *args, **kw) self.file_select_popup = None self.getOptionValues() try: size_index = paper_types.index(self.paper_type) except: size_index = 0 try: other_unit_index = paper_units.index(self.other_unit) except: other_unit_index = 0 try: orientation_index = paper_orientations.index(self.paper_orientation) except: orientation_index = 0 try: style_index = style_choices.index(self.output_style) except: style_index = 0 try: format_index = format_choices.index(self.output_format) except: format_index = 0 if haveTicks: try: tick_location_index = tick_locations.index(self.tick_location) except: tick_location_index = 0 self.grid_columnconfigure(1, weight=1) row = 0 button = Button(self, text='File:', command=self.findFile, tipText='Select location to save print file') button.grid(row=row, column=0, sticky='e') self.file_entry = Entry(self, width=40, text=self.file_name, tipText='Location where file is saved on disk') self.file_entry.grid(row=row, column=1, sticky='ew') row += 1 label = Label(self, text='Title:') label.grid(row=row, column=0, sticky='e') self.title_entry = Entry(self, width=40, text=self.title, tipText='Title of the printout, displayed at top') self.title_entry.grid(row=row, column=1, sticky='ew') row += 1 label = Label(self, text='X axis label:') label.grid(row=row, column=0, sticky='e') self.x_axis_entry = Entry(self, width=40, text=self.x_axis_label, tipText='X axis label for the printout') self.x_axis_entry.grid(row=row, column=1, sticky='ew') row += 1 label = Label(self, text='Y axis label:') label.grid(row=row, column=0, sticky='e') self.y_axis_entry = Entry(self, width=40, text=self.y_axis_label, tipText='Y axis label for the printout') self.y_axis_entry.grid(row=row, column=1, sticky='ew') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=2, sticky='ew') frame.grid_columnconfigure(4, weight=1) label = Label(frame, text='Paper size:') label.grid(row=0, column=0, sticky='e') entries = [] for t in paper_types: if t == Output.other_paper_type: entry = t else: (w, h, u) = paper_sizes[t] entry = t + ' (%2.1f %s x %2.1f %s)' % (w, u, h, u) entries.append(entry) self.size_menu = PulldownList(frame, callback=self.changedSize, texts=entries, index=size_index, tipText='The paper size for the printout') self.size_menu.grid(row=0, column=1, sticky='w') self.other_frame = Frame(frame) self.other_frame.grid_columnconfigure(0, weight=1) self.other_entry = FloatEntry(self.other_frame, text=self.other_size, isArray=True, tipText='The size of the Other paper in both dimensions; this requires two values, space or comma separated') self.other_entry.grid(row=0, column=0, sticky='ew') self.other_unit_menu= PulldownList(self.other_frame, texts=paper_units, index=other_unit_index, tipText='The unit for the Other paper size') self.other_unit_menu.grid(row=0, column=1, sticky='ew') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) frame.grid_columnconfigure(3, weight=1) frame.grid_columnconfigure(5, weight=1) label = Label(frame, text='Orientation:') label.grid(row=0, column=0, sticky='e') self.orientation_menu = PulldownList(frame, texts=paper_orientations, index=orientation_index, tipText='Whether the paper should be set in Portrait or Landscape mode') self.orientation_menu.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Style:') label.grid(row=0, column=2, sticky='e') self.style_menu = PulldownList(frame, texts=style_choices, index=style_index, tipText='Whether the printout should be in colour or black and white') self.style_menu.grid(row=0, column=3, sticky='w') label = Label(frame, text=' Format:') label.grid(row=0, column=4, sticky='e') self.format_menu = PulldownList(frame, callback=self.changedFormat, texts=format_choices, index=format_index, tipText='Whether to save as PS, EPS or PDF') self.format_menu.grid(row=0, column=5, sticky='w') if haveTicks: row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) frame.grid_columnconfigure(3, weight=1) label = Label(frame, text='Tick Location:') label.grid(row=0, column=0, sticky='e') self.tick_menu = PulldownList(frame, texts=tick_locations, index=tick_location_index, tipText='Whether the tick marks appear on the inside or outside of the frame') self.tick_menu.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Tick Placement:') label.grid(row=0, column=2, sticky='e') if self.tick_placement is None: selected = None else: selected = [(x in self.tick_placement) for x in tick_placements] self.tick_buttons = CheckButtons(frame, entries=tick_placements, selected=selected, tipTexts=('Whether the tick marks appear on the top and/or bottom and/or left and/or right',)) self.tick_buttons.grid(row=0, column=3, sticky='w') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) frame.grid_columnconfigure(3, weight=1) label = Label(frame, text='Tick Font:') label.grid(row=0, column=0, sticky='e') self.tick_font_list = FontList(frame, mode='Print', selected=self.tick_font, extraTexts=[PrintTicks.no_tick_text], tipText='The font used for the tick mark labels') self.tick_font_list.grid(row=0, column=1, sticky='w') label = Label(frame, text='Tick Spacing:') label.grid(row=0, column=2, sticky='e') # TBD: put preferred choice in data model self.spacing_menu = PulldownList(frame, texts=spacing_choices, index=0, callback=self.changedSpacing, tipText='Whether the program should automatically calculate the major/minor tick spacings and how many decimal places are used for the ticks, or whether the these are specified manually') self.spacing_menu.grid(row=0, column=3, sticky='w') ff = self.spacing_frame = Frame(frame) ff.grid_columnconfigure(1, weight=1) ff.grid_columnconfigure(2, weight=1) label = Label(ff, text='Tick Spacing') label.grid(row=0, column=0, sticky='w') label = Label(ff, text='Major') label.grid(row=0, column=1, sticky='ew') label = Label(ff, text='Minor') label.grid(row=0, column=2, sticky='ew') label = Label(ff, text='Decimals') label.grid(row=0, column=3, sticky='ew') label = Label(ff, text='X:') label.grid(row=1, column=0, sticky='w') self.x_major_entry = FloatEntry(ff, tipText='The spacing in display units of the major tick marks in the X dimension') self.x_major_entry.grid(row=1, column=1, sticky='ew') self.x_minor_entry = FloatEntry(ff, tipText='The spacing in display units of the minor tick marks in the X dimension (not printed if left blank)') self.x_minor_entry.grid(row=1, column=2, sticky='ew') self.x_decimal_entry = IntEntry(ff, tipText='The number of decimal places for the tick numbers in the X dimension') self.x_decimal_entry.grid(row=1, column=3, sticky='ew') label = Label(ff, text='Y:') label.grid(row=2, column=0, sticky='w') self.y_major_entry = FloatEntry(ff, tipText='The spacing in display units of the major tick marks in the Y dimension') self.y_major_entry.grid(row=2, column=1, sticky='ew') self.y_minor_entry = FloatEntry(ff, tipText='The spacing in display units of the minor tick marks in the Y dimension (not printed if left blank)') self.y_minor_entry.grid(row=2, column=2, sticky='ew') self.y_decimal_entry = IntEntry(ff, tipText='The number of decimal places for the tick numbers in the Y dimension') self.y_decimal_entry.grid(row=2, column=3, sticky='ew') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) label = Label(frame, text='Tick Length:') label.grid(row=0, column=0, sticky='e') # TBD: put preferred choice in data model self.tick_length_menu = PulldownList(frame, texts=tick_length_choices, index=0, callback=self.changedLength, tipText='Whether the program should automatically calculate the major/minor tick lengths, or whether the these are specified manually') self.tick_length_menu.grid(row=0, column=1, sticky='w') ff = self.length_frame = Frame(frame) ff.grid_columnconfigure(1, weight=1) label = Label(ff, text=' Major length:') label.grid(row=0, column=0, sticky='w') self.length_major_entry = FloatEntry(ff, tipText='The length in points of the major tick marks') self.length_major_entry.grid(row=0, column=1, sticky='w') label = Label(ff, text='Minor length:') label.grid(row=0, column=2, sticky='w') self.length_minor_entry = FloatEntry(ff, tipText='The length in points of the minor tick marks') self.length_minor_entry.grid(row=0, column=3, sticky='w') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(3, weight=1) frame.grid_columnconfigure(4, weight=1) label = Label(frame, text='Scaling:') label.grid(row=0, column=0, sticky='e') # TBD: put preferred choice in data model self.scaling_menu = PulldownList(frame, texts=scaling_choices, index=0, callback=self.changedScaling, tipText='Whether the plot should be scaled as a percentage of the maximum size that would fit on the paper, or instead should be specified by the number of cms or inches per unit') self.scaling_menu.grid(row=0, column=1, sticky='ew') self.scaling_scale = Scale(frame, orient=Tkinter.HORIZONTAL, value=self.scaling, tipText='The percentage of the maximum size that would fit on the paper that the plot is scaled by') self.scaling_scale.grid(row=0, column=2, columnspan=3, sticky='ew') self.x_scaling_label = Label(frame, text='X:') self.x_scaling_entry = FloatEntry(frame, tipText='The scaling that should be used in the X dimension as cms or inches per unit') self.y_scaling_label = Label(frame, text='Y:') self.y_scaling_entry = FloatEntry(frame, tipText='The scaling that should be used in the Y dimension as cms or inches per unit') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='w') frame.grid_columnconfigure(2, weight=1) label = Label(frame, text='Include:') label.grid(row=0, column=0, sticky='e') tipTexts = ('Whether the time and date should be included in the printout', 'Whether the file name should be included in the printout') if self.border_decoration is None: selected = None else: selected = [(x in self.border_decoration) for x in border_decorations] self.border_buttons = CheckButtons(frame, entries=border_decorations, selected=selected, tipTexts=tipTexts) self.border_buttons.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Using Font:') label.grid(row=0, column=2, sticky='e') self.border_font_list = FontList(frame, mode='Print', selected=self.border_font, tipText='The font used for the border texts') self.border_font_list.grid(row=0, column=3, sticky='w') row += 1 label = Label(self, text='Line width:') label.grid(row=row, column=0, sticky='w') self.linewidth_entry = FloatEntry(self, width=10, text=self.linewidth, tipText='Line width for drawing') self.linewidth_entry.grid(row=row, column=1, sticky='w') def destroy(self): self.setOptionValues() if self.file_select_popup: self.file_select_popup.destroy() Frame.destroy(self) def getOptionValues(self): getOption = self.getOption if getOption: file_name = getOption('FileName', defaultValue='') title = getOption('Title', defaultValue='') x_axis_label = getOption('XAxisLabel', defaultValue='') y_axis_label = getOption('YAxisLabel', defaultValue='') paper_type = getOption('PaperSize', defaultValue=paper_types[0]) paper_type = paper_type_dict.get(paper_type, paper_types[0]) other_height = getOption('OtherHeight', defaultValue=10) other_width = getOption('OtherWidth', defaultValue=10) other_size = [other_height, other_width] other_unit = getOption('OtherUnit', defaultValue=paper_units[0]) paper_orientation = getOption('Orientation', defaultValue=paper_orientations[0]) in_color = getOption('InColor', defaultValue=True) if in_color: output_style = style_choices[0] else: output_style = style_choices[1] format_option = getOption('OutputFormat', defaultValue=format_options[0]) output_format = format_choices[format_options.index(format_option)] if self.haveTicks: tick_outside = getOption('TickOutside', defaultValue=tick_locations[0]) if tick_outside: tick_location = tick_locations.index(PrintTicks.Outside) else: tick_location = tick_locations.index(PrintTicks.Inside) tick_placement = getTickPlacement1(getOption('TickPlacement', defaultValue='nsew')) dateTime = getOption('ShowsDateTime', defaultValue=True) fileName = getOption('ShowsFileName', defaultValue=True) border_font = getOption('BorderFont', defaultValue='Helvetica 10') border_decoration = [] if dateTime: border_decoration.append(border_decorations[0]) if fileName: border_decoration.append(border_decorations[1]) if self.haveTicks: spacing_choice = getOption('SpacingChoice', defaultValue=spacing_choices[0]) x_major = getOption('XMajor', defaultValue=1.0) x_minor = getOption('XMinor', defaultValue=1.0) x_decimal = getOption('XDecimal', defaultValue=3) y_major = getOption('YMajor', defaultValue=1.0) y_minor = getOption('YMinor', defaultValue=1.0) y_decimal = getOption('YDecimal', defaultValue=3) tick_length_choice = getOption('TickLengthChoice', defaultValue=tick_length_choices[0]) tick_major = getOption('TickMajor', defaultValue=10) tick_minor = getOption('TickMinor', defaultValue=5) scaling_choice = getOption('ScalingChoice', defaultValue=scaling_choices[0]) scaling = getOption('Scaling', defaultValue=0.7) scaling = int(round(100.0 * scaling)) x_scaling = getOption('XScaling', defaultValue=1.0) y_scaling = getOption('YScaling', defaultValue=1.0) if self.haveTicks: tick_font = getOption('TickFont', defaultValue='Helvetica 10') linewidth = getOption('LineWidth', defaultValue=Output.default_linewidth) else: file_name = '' title = '' x_axis_label = '' y_axis_label = '' paper_type = paper_types[0] other_unit = paper_units[0] other_size = '' paper_orientation = paper_orientations[0] output_style = style_choices[0] output_format = format_choices[0] if self.haveTicks: tick_location = tick_locations[0] tick_placement = tick_placements border_decoration = border_decorations border_font = 'Helvetica 10' if self.haveTicks: spacing_choice = spacing_choices[0] x_major = 1.0 x_minor = 1.0 x_decimal = 3 y_major = 1.0 y_minor = 1.0 y_decimal = 3 tick_length_choice = tick_length_choices[0] tick_major = 10 tick_minor = 5 scaling_choice = scaling_choices[0] scaling = 70 x_scaling = 1.0 y_scaling = 1.0 if self.haveTicks: tick_font = 'Helvetica 10' linewidth = Output.default_linewidth if not self.haveTicks: tick_location = None tick_placement = None spacing_choice = spacing_choices[0] x_major = 1.0 x_minor = 1.0 x_decimal = 3 y_major = 1.0 y_minor = 1.0 y_decimal = 3 tick_font = 'Helvetica 10' tick_length_choice = tick_length_choices[0] tick_major = 10 tick_minor = 5 self.file_name = file_name self.title = title self.x_axis_label = x_axis_label self.y_axis_label = y_axis_label self.paper_type = paper_type self.other_unit = other_unit self.other_size = other_size self.paper_orientation = paper_orientation self.output_style = output_style self.output_format = output_format self.tick_location = tick_location self.tick_placement = tick_placement self.border_decoration = border_decoration self.border_font = border_font self.spacing_choice = spacing_choice self.x_major = x_major self.x_minor = x_minor self.x_decimal = x_decimal self.y_major = y_major self.y_minor = y_minor self.y_decimal = y_decimal self.scaling_choice = scaling_choices[0] self.scaling = scaling self.x_scaling = x_scaling self.y_scaling = y_scaling self.tick_font = tick_font self.linewidth = linewidth self.tick_length_choice = tick_length_choice self.tick_major = tick_major self.tick_minor = tick_minor def setOptionValues(self): if not hasattr(self, 'file_entry'): # it looks like on destroy can have function called but file_entry deleted already return self.file_name = file_name = self.file_entry.get() self.title = title = self.title_entry.get() self.x_axis_label = x_axis_label = self.x_axis_entry.get() self.y_axis_label = y_axis_label = self.y_axis_entry.get() n = self.size_menu.getSelectedIndex() self.paper_type = paper_type = paper_types[n] if paper_type == Output.other_paper_type: other_size = self.other_entry.get() other_unit = self.other_unit_menu.getText() else: other_size = None other_unit = None self.other_size = other_size self.other_unit = other_unit self.paper_orientation = paper_orientation = self.orientation_menu.getText() self.output_style = output_style = self.style_menu.getText() self.output_format = output_format = self.format_menu.getText() if self.haveTicks: tick_location = self.tick_menu.getText() tick_placement = self.tick_buttons.getSelected() else: tick_location = tick_placement = None self.tick_location = tick_location self.tick_placement = tick_placement self.border_decoration = border_decoration = self.border_buttons.getSelected() self.border_font = border_font = self.border_font_list.getText() if self.haveTicks: self.spacing_choice = spacing_choice = self.spacing_menu.getText() if spacing_choice != spacing_choices[0]: self.x_major = self.x_major_entry.get() self.x_minor = self.x_minor_entry.get() self.x_decimal = self.x_decimal_entry.get() self.y_major = self.y_major_entry.get() self.y_minor = self.y_minor_entry.get() self.y_decimal = self.y_decimal_entry.get() self.tick_length_choice = tick_length_choice = self.tick_length_menu.getText() if tick_length_choice != tick_length_choices[0]: self.tick_major = self.length_major_entry.get() self.tick_minor = self.length_minor_entry.get() self.scaling_choice = scaling_choice = self.scaling_menu.getText() if self.scaling_choice == scaling_choices[0]: scaling = self.scaling_scale.get() self.scaling = int(round(scaling)) else: self.x_scaling = self.x_scaling_entry.get() self.y_scaling = self.y_scaling_entry.get() if self.haveTicks: self.tick_font = self.tick_font_list.getText() self.linewidth = self.linewidth_entry.get() setOption = self.setOption if setOption: setOption('FileName', value=file_name) setOption('Title', value=title) setOption('XAxisLabel', value=x_axis_label) setOption('YAxisLabel', value=y_axis_label) if paper_type == Output.other_paper_type: setOption('OtherHeight', value=other_size[0]) setOption('OtherWidth', value=other_size[1]) setOption('OtherUnit', value=other_unit) else: paper_type = paper_type_inverse_dict[paper_type] setOption('PaperSize', value=paper_type) setOption('Orientation', value=paper_orientation) in_color = (output_style == style_choices[0]) setOption('InColor', value=in_color) output_format = format_options[format_choices.index(output_format)] setOption('OutputFormat', value=output_format) if self.haveTicks: tick_outside = (tick_location == PrintTicks.Outside) setOption('TickOutside', value=tick_outside) tick_placement = getTickPlacement2(tick_placement) setOption('TickPlacement', value=tick_placement) dateTime = (border_decorations[0] in border_decoration) fileName = (border_decorations[1] in border_decoration) setOption('ShowsDateTime', value=dateTime) setOption('ShowsFileName', value=fileName) setOption('BorderFont', value=border_font) if self.haveTicks: setOption('SpacingChoice', value=spacing_choice) if spacing_choice != spacing_choices[0]: setOption('XMajor', self.x_major) setOption('XMinor', self.x_minor) setOption('XDecimal', self.x_decimal) setOption('YMajor', self.y_major) setOption('YMinor', self.y_minor) setOption('YDecimal', self.y_decimal) setOption('TickLengthChoice', value=tick_length_choice) if tick_length_choice != tick_length_choices[0]: setOption('TickMajor', self.tick_major) setOption('TickMinor', self.tick_minor) setOption('ScalingChoice', value=scaling_choice) if scaling_choice == scaling_choices[0]: setOption('Scaling', value=0.01*self.scaling) else: setOption('XScaling', value=self.x_scaling) setOption('YScaling', value=self.y_scaling) if self.haveTicks: setOption('TickFont', self.tick_font) setOption('LineWidth', self.linewidth) def findFile(self): if self.file_select_popup: self.file_select_popup.open() else: file_types = [ FileType('All', ['*']), FileType('PostScript', ['*.ps', '*.eps']), FileType('PDF', ['*.pdf', '*.ai']) ] self.file_select_popup = FileSelectPopup(self, file_types=file_types) file = self.file_select_popup.getFile() if file: self.file_entry.set(file) def changedSize(self, entry): if entry == Output.other_paper_type: self.other_frame.grid(row=0, column=2, columnspan=2, sticky='w') else: self.other_frame.grid_forget() def changedFormat(self, entry): file_suffix = file_suffixes.get(entry) if not file_suffix: return file_name = self.file_entry.get() if not file_name: return for suffix in format_suffixes: if file_name.endswith(suffix): if suffix != file_suffix: n = len(suffix) file_name = file_name[:-n] + file_suffix self.file_entry.set(file_name) break else: file_name = file_name + file_suffix self.file_entry.set(file_name) def changedScaling(self, choice): if choice == scaling_choices[0]: self.scaling_scale.grid(row=0, column=2, columnspan=3, sticky='ew') self.x_scaling_label.grid_forget() self.x_scaling_entry.grid_forget() self.y_scaling_label.grid_forget() self.y_scaling_entry.grid_forget() else: self.scaling_scale.grid_forget() self.x_scaling_label.grid(row=0, column=2, sticky='w') self.x_scaling_entry.grid(row=0, column=3, columnspan=2, sticky='ew') self.y_scaling_label.grid(row=1, column=2, sticky='w') self.y_scaling_entry.grid(row=1, column=3, columnspan=2, sticky='ew') self.setOptionValues() def changedSpacing(self, choice): if choice == spacing_choices[0]: self.spacing_frame.grid_forget() else: self.spacing_frame.grid(row=1, column=1, columnspan=3, sticky='ew') self.setOptionValues() def changedLength(self, choice): if choice == tick_length_choices[0]: self.length_frame.grid_forget() else: self.length_frame.grid(row=1, column=0, columnspan=4, sticky='ew') self.setOptionValues() # width and height are of plot, in pixels def getOutputHandler(self, pixel_width, pixel_height, unit_width=1.0, unit_height=1.0, fonts=None): if not fonts: fonts = [] else: fonts = list(fonts) for n in range(len(fonts)): if fonts[n] == 'Times': fonts[n] = 'Times-Roman' self.setOptionValues() if not self.file_name: showError('No file', 'No file specified', parent=self) return None x_scaling = y_scaling = 1.0 if self.scaling_choice != scaling_choices[0]: try: x_scaling = float(self.x_scaling) except: showError('Bad X Scaling', 'Specified X Scaling must be floating point', parent=self) return None try: y_scaling = float(self.y_scaling) except: showError('Bad Y Scaling', 'Specified Y Scaling must be floating point', parent=self) return None if os.path.exists(self.file_name): if not showYesNo('File exists', 'File "%s" exists, overwrite?' % self.file_name, parent=self): return None if self.paper_type == Output.other_paper_type: paper_size = self.other_size + [ self.other_unit ] else: paper_size = paper_sizes[self.paper_type] output_scaling = self.scaling / 100.0 border_font = self.border_font (font, size) = border_font.split() size = int(size) border_text = {} for decoration in self.border_decoration: if decoration == 'Time and Date': location = 'se' text = time.ctime(time.time()) elif decoration == 'File Name': location = 'sw' text = self.file_name else: continue # should not be here border_text[location] = (text, font, size) if self.title: location = 'n' border_text[location] = (self.title, font, size+6) if font not in fonts: fonts.append(font) if self.haveTicks and self.tick_location == PrintTicks.Outside: axis_label_offset = 2 else: axis_label_offset = 0 outputHandler = PrintHandler.getOutputHandler(self.file_name, pixel_width, pixel_height, unit_width, unit_height, scaling_choice=self.scaling_choice, output_scaling=output_scaling, w_scaling=x_scaling, h_scaling=y_scaling, paper_size=paper_size, paper_orientation=self.paper_orientation, output_style=self.output_style, output_format=self.output_format, border_text=border_text, x_axis_label=self.x_axis_label, y_axis_label=self.y_axis_label, axis_label_font=font, axis_label_size=size, axis_label_offset=axis_label_offset, fonts=fonts, linewidth=self.linewidth, do_outline_box=self.doOutlineBox) return outputHandler def getAspectRatio(self): self.setOptionValues() if self.paper_type == Output.other_paper_type: paper_size = self.other_size else: paper_size = paper_sizes[self.paper_type] r = paper_size[1] / paper_size[0] if self.paper_orientation == 'Landscape': r = 1.0 / r return r
class PeakSeparatorGui(BasePopup): """ **Separate Merged Peaks Using Peak Models** The Peak Separator code uses a Markov Chain Monte Carlo search which, using idealised peak shapes, attempts to deconvolve overlapped peak regions into their separate constituent peaks. This routine is also suitable for accurately fitting model shapes to single peaks in order to calculate precise intensities. **Options Peak Separator Parameters** *Min. Number of peaks* is by default set to one, it is not possible to set this to a value less than one. *Max. Number of peaks* is by default set to one, increasing this value allows the search routine to fit more models. The best fit may be found with fewer than the maximum number models. Higher numbers slow the routine, and setting this value to 0 allows the routine to (effectively) fit unlimited peaks. *Only pick positive peaks*. If you are not interested in negative peaks, removing the possibility of fitting negative peaks can reduce search time. *Peak Model* fits the spectra with either a Gaussian peak model or a Lorentzian peak model. **Options Region** *Peak List* choose which peak list newly picked peaks should be added to. Peaks picked using this method will have their details appended with 'PeakSepartor' so you know where they came from. *Region Table* shows which area of the current spectrum is about to be searched. *Add Region*. Once an area of spectra has been highlighted clicking this button will pass it's details on to the Peak Separator. *Reset All* will reset all search parameters. *Separate Peaks* will run the Peak Separator code with your current settings. This may take a few minutes to run, depending on the size of the spectral region being searched, the number of peaks being fitted and the speed of your machine. Please wait while this completes. After a successful Peak Separation run, the found peaks will be added to the selected peak list. These peaks intensties (volume) have been found using the peak model selected. **Advanced Settings Tab** *Rate* affects the speed of the Markov Chain Monte Carlo routine. A smaller value results in longer execution, but possibly higher quality results. The default setting is deemed sensible for the majority of runs. *Line Width* offers a finer degree of control over maximum and minimum peak widths for each dimension. The default values are *very* stupid and could do with re-checking for each experiment. *Re-Pick Entire Peak List* if you would like to use the Peak Separator to repick *every* peak in your peak list, try this option - but note that this may take a very long time! """ def __init__(self, parent, programName='Peak Separator', **kw): self.parent = parent self.programName = programName self.versionInfo = 'Version 0.2' self.help_url = 'http://www.ccpn.ac.uk/' self.window = None self.waiting = False self.rootWindow = None # just used for display - PeakSeparator will not see this self._minSigmaHz = None self._maxSigmaHz = None self.customSigma = False self.rePickPeakList = False self._sampleStartPpm = None self._sampleEndPpm = None try: self.project = parent.project except: pass self.params = PeakSeparatorParams() BasePopup.__init__(self, parent=parent, title=programName, location='+100+100', **kw) if not self.analysisProject: print '&&& init: No analysis project found ...' try: if parent.argumentServer: self.argServer = parent.argumentServer else: print '&&& init: No argument server found...' except: print '&&& init: Test' ########################################################################### def body(self, guiFrame): self.geometry('450x500') guiFrame.grid_rowconfigure(0, weight=1) guiFrame.grid_columnconfigure(0, weight=1) options = ['Peak Separator', 'Advanced Settings'] tabbedFrame = TabbedFrame(guiFrame, options=options) tabbedFrame.grid(row=0, column=0, sticky='nsew') buttons = UtilityButtonList(tabbedFrame.sideFrame, helpUrl=self.help_url) buttons.grid(row=0, column=0, sticky='e') self.tabbedFrame = tabbedFrame frameA, frameB = tabbedFrame.frames # # FrameA : Main Settings # frameA.grid_columnconfigure(1, weight=1) row = 0 # Label row row += 1 div = LabelDivider(frameA, text='Peak Separator Parameters') div.grid(row=row, column=0, columnspan=2, sticky='ew') row += 1 label = Label(frameA, text='Min. number of peaks:') label.grid(row=row, column=0, sticky='w') self.minPeaksEntry = IntEntry(frameA, returnCallback=self.applyChange, width=10, \ tipText='Minimum number of peaks to find (must be > 0)') self.minPeaksEntry.grid(row=row, column=1, sticky='n') self.minPeaksEntry.bind('<Leave>', self.applyChange, '+') row += 1 label = Label(frameA, text='Max. number of peaks:') label.grid(row=row, column=0, sticky='w') self.maxPeaksEntry = IntEntry(frameA, returnCallback=self.applyChange, width=10, \ tipText='Maximum number of peaks to find (0 is unlimited - not recommended)') self.maxPeaksEntry.grid(row=row, column=1, sticky='n') self.maxPeaksEntry.bind('<Leave>', self.applyChange, '+') row += 1 label = Label(frameA, text='Only pick positive peaks:') label.grid(row=row, column=0, sticky='w') entries = ['False', 'True'] self.posPeaksButtons = RadioButtons( frameA, entries=entries, select_callback=self.applyChange, direction='horizontal', tipTexts=[ 'Search for both positive and negative intensity peaks', 'Limit search to only positive peaks' ]) self.posPeaksButtons.grid(row=row, column=1, sticky='n') row += 1 label = Label(frameA, text='Peak Model:') label.grid(row=row, column=0, sticky='w') ### G/L Mixture works, but volume calculation involves Gamma function # entries = ['Gaussian', 'Lorentzian', 'G/L Mixture'] entries = ['Gaussian', 'Lorentzian'] self.shapeButtons = RadioButtons( frameA, entries=entries, select_callback=self.applyChange, direction='horizontal', tipTexts=[ 'Choose a Gaussian model peak shape to fit to peaks', 'Choose a Lorentzian model peak shape to fit to peaks' ]) self.shapeButtons.grid(row=row, column=1, sticky='n') row += 1 div = LabelDivider(frameA, text='Region', tipText='Region that search will limit itself to') div.grid(row=row, column=0, columnspan=2, sticky='ew') row += 1 label = Label(frameA, text='Peak List:') label.grid(row=row, column=0, sticky='nw') self.peakListPulldown = PulldownList( frameA, callback=self.setManuallyPickPeakList, tipText='Select which peak list new peaks are to be added to') self.peakListPulldown.grid(row=row, column=1, sticky='nw') # tricky scrolled matrix row += 1 self.regionTable = None frameA.grid_rowconfigure(row, weight=1) headings = ('dim.', 'start (ppm)', 'end (ppm)', 'actual size') self.editDimEntry = IntEntry(self, returnCallback=self.applyChange, width=5, tipText='Dimension number') self.editStartEntry = FloatEntry(self, returnCallback=self.applyChange, width=5, tipText='Search area lower bound') self.editEndEntry = FloatEntry(self, returnCallback=self.applyChange, width=5, tipText='Search area upper bound') editWidgets = [ self.editDimEntry, self.editStartEntry, self.editEndEntry, None ] editGetCallbacks = [None, None, None, None] editSetCallbacks = [None, None, None, None] self.regionTable = ScrolledMatrix(frameA, headingList=headings, multiSelect=False, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, initialRows=5) self.regionTable.grid(row=row, column=0, columnspan=2, sticky='nsew') # Run Button row += 1 texts = ['Add Region'] commands = [self.updateFromRegion] self.addResetButtons = ButtonList( frameA, texts=texts, commands=commands, tipTexts=['Add selected specrtral region']) self.addResetButtons.grid(row=row, column=0, columnspan=2, sticky='ew') row += 1 texts = ['Separate Peaks'] commands = [self.runPeakSeparator] self.runButton = ButtonList(frameA, texts=texts, commands=commands, expands=True, tipTexts=['Run peak search now']) self.runButton.grid(row=row, column=0, columnspan=2, sticky='nsew') # # FrameB : Further Settings # frameB.grid_columnconfigure(0, weight=1) row = 0 div = LabelDivider(frameB, text='Rate:') div.grid(row=row, column=0, columnspan=2, sticky='ew') row += 1 label = Label(frameB, text='Rate of MCMC step size change') label.grid(row=row, column=0, columnspan=1, sticky='w') self.rateEntry = FloatEntry(frameB, returnCallback=self.applyChange, width=10, \ tipText='Rate effects speed of run, smaller values take longer but may produce better results') self.rateEntry.grid(row=row, column=1, sticky='n') self.rateEntry.bind('<Leave>', self.applyChange, '+') self.rateEntry.set(self.params.rate) # tricky scrolled matrix for line width row += 2 div = LabelDivider(frameB, text='Line Width (Hz):') div.grid(row=row, column=0, columnspan=2, sticky='ew') row += 1 label = Label(frameB, text="Descr.") label.grid(row=row, rowspan=2, column=0, sticky='w') row += 1 self.lineWidthTable = None frameB.grid_rowconfigure(row, weight=1) lineWidthHeadings = ('dim.', 'min. σ (Hz)', 'max. σ (Hz)') self.editMinSigmaEntry = FloatEntry(self, returnCallback=self.applyChange, width=5, tipText='Minimum line width (Hz)') self.editMaxSigmaEntry = FloatEntry(self, returnCallback=self.applyChange, width=5, tipText='Maximum line width (Hz)') # self.editDimEntry is also from regionTable initialWidthRows = 4 editLineWidthWidgets = [ None, self.editMinSigmaEntry, self.editMaxSigmaEntry ] editLineWidthGetCallbacks = [None, self.getSigmaMin, self.getSigmaMax] editLineWidthSetCallbacks = [None, self.setSigmaMin, self.setSigmaMax] self.lineWidthTable = ScrolledMatrix( frameB, headingList=lineWidthHeadings, multiSelect=False, editWidgets=editLineWidthWidgets, editGetCallbacks=editLineWidthGetCallbacks, editSetCallbacks=editLineWidthSetCallbacks, initialRows=initialWidthRows) self.lineWidthTable.grid(row=row, column=0, columnspan=2, sticky='nsew') # option to 'repick' exisiting peak list row += initialWidthRows div = LabelDivider(frameB, text='(optional - repick entire peak list)') div.grid(row=row, column=0, columnspan=2, sticky='ew') row += 1 self.repickListPulldown = PulldownList( frameB, callback=self.setRePickPeakList, tipText= 'Select which peak list to repick (new peaks will be put into a new peak list)' ) self.repickListPulldown.grid(row=row, column=0, sticky='nw') texts = ['Repick Peak List'] commands = [self.runRepickPeaks] self.runButton = ButtonList( frameB, texts=texts, commands=commands, expands=True, tipTexts=['Repick selected peak list into a new peak list.']) self.runButton.grid(row=row, column=1, columnspan=1, sticky='nsew') row += 1 div = LabelDivider(frameB) row += 1 texts = ['Separate Peaks'] commands = [self.runPeakSeparator] self.runButton = ButtonList(frameB, texts=texts, commands=commands, expands=True, tipTexts=['Run peak search now']) self.runButton.grid(row=row, column=0, columnspan=2, sticky='nsew') self.setWidgetEntries() self.administerNotifiers(self.registerNotify) def administerNotifiers(self, notifyFunc): for func in ('__init__', 'delete'): notifyFunc(self.updateAfter, 'ccp.nmr.Nmr.PeakList', func) notifyFunc(self.updateAfter, 'ccp.nmr.Nmr.Experiment', 'setName') notifyFunc(self.updateAfter, 'ccp.nmr.Nmr.DataSource', 'setName') def destroy(self): self.administerNotifiers(self.unregisterNotify) BasePopup.destroy(self) ########################################################################### # update parameters from PS Region def updateFromRegion(self): if not self.params.peakList: print '&&& update from region: Need a peak list' return if (self.argServer.parent.currentRegion) == None: showError('No Region', 'Please select a peak region to be separated') return self.rePickPeakList = False getRegionParams(self.params, argServer=self.argServer) if not self.customSigma: self.initSigmaParams() self.setWidgetEntries() ########################################################################### # update parameters from PS PeakList def updateFromPeakList(self): if not self.params.peakList: print '&&& update from peakList: Need a peak list' return getPeakListParams(self.params) if not self.customSigma: self.initSigmaParams() self.setWidgetEntries() ########################################################################### # Run the C library! def runPeakSeparator(self): """ run the peak separator """ # hack for Macs - focus isn't always lost on mouse move # so bind event not always called. Shouldn't affect other OS. self.applyChange() if not self.params.peakList: print '&&& Peak list not yet set' else: # SeparatePeakRoutine(self.params, self.params.peakList, routine='pymc' ) SeparatePeakRoutine(self.params, self.params.peakList, routine='bayesys') def runRepickPeaks(self): """ Run the Peak Separator on entire chosen peak list """ # hack for Macs - focus isn't always lost on mouse move # so bind event not always called. Shouldn't affect other OS. self.applyChange() if not self.params.peakList: print '&&& Peak list not yet set' else: SeparatePeaksInPeakList(self.params) ########################################################################### def setWidgetEntries(self): ### Page One widgets self.minPeaksEntry.set(self.params.minAtoms) self.maxPeaksEntry.set(self.params.maxAtoms) if self.params.positivePeaks == 1: self.posPeaksButtons.set('True') # only pick pos peaks else: self.posPeaksButtons.set('False') # do something fancy if different shapes for each dim! n = self.params.peakShape - 3 # shape is only 3, 4, (5) self.shapeButtons.setIndex(n) if self.project is not None: self.updatePeakListList() self.updateSpectrumWindow() if self.params.sampleStart and self.params.peakList: if not self.rePickPeakList: objectList = [] textMatrix = [] if len(self.params.samplePpmStart) != self.params.Ndim: return for i in range(self.params.Ndim): dim_entry = [] dim_entry.append('%2d' % (i + 1)) dim_entry.append('%7.3f' % self.params.samplePpmStart[i]) dim_entry.append('%7.3f' % self.params.samplePpmEnd[i]) dim_entry.append('%3d' % self.params.sampleSize[i]) textMatrix.append(dim_entry) self.regionTable.update(textMatrix=textMatrix, objectList=objectList) ### Page Two widgets self.rateEntry.set(self.params.rate) if self.params.peakList and self.params.Ndim: textMatrix = [] objectList = [] for i in range(self.params.Ndim): if self.params.isFreqDim[i]: dim_entry = [] objectList.append(i) dim_entry.append('%2d' % (i + 1)) dim_entry.append('%7.3f' % self._minSigmaHz[i]) dim_entry.append('%7.3f' % self._maxSigmaHz[i]) textMatrix.append(dim_entry) self.lineWidthTable.update(textMatrix=textMatrix, objectList=objectList) def applyChange(self, *event): """ Upon change, add settings to params """ # Page One apply changes self.params.minAtoms = self.minPeaksEntry.get() self.params.maxAtoms = self.maxPeaksEntry.get() if self.posPeaksButtons.get() == 'True': # asked only pick pos peaks self.params.positivePeaks = 1 else: self.params.positivePeaks = 0 # do something fancy if different shapes for each dim! n = self.shapeButtons.getIndex() # shape is only 3, 4, (5) self.params.peakShape = n + 3 # Page Two apply changes self.params.rate = float(self.rateEntry.get()) self.updateSigmaParams() ########################################################################### # Peak list functions provide PeakSeparator some inherited params def getPeakListList(self): """ given a spectrum, get list of peak lists """ project = self.project peakLists = [] for experiment in self.nmrProject.experiments: for spectrum in experiment.dataSources: for peakList in spectrum.peakLists: peakLists.append([ '%s:%s:%d' % (experiment.name, spectrum.name, peakList.serial), peakList ]) peakLists.sort() return peakLists def updatePeakListList(self): """ set the peaklist list in the pulldown menu """ peakListData = self.getPeakListList() index = -1 names = [] peakList = self.params.peakList if peakListData: names = [x[0] for x in peakListData] peakLists = [x[1] for x in peakListData] if peakList not in peakLists: peakList = peakLists[0] index = peakLists.index(peakList) else: peakList = None peakLists = [] if peakList is not self.params.peakList: self.params.peakList = peakList self.peakListPulldown.setup(names, peakLists, index) self.repickListPulldown.setup(names, peakLists, index) def setRePickPeakList(self, peakList): """ Set the peak list to be repicked (and hit a Flag) """ self.rePickPeakList = True self.setPeakList(peakList) def setManuallyPickPeakList(self, peakList): """ Set the peak list to add new peaks to (and hit a Flag) """ self.rePickPeakList = False self.setPeakList(peakList) def setPeakList(self, peakList): """ Sets the Peak List """ if peakList is not self.params.peakList: self.params.peakList = peakList # # interrogate the peak list and get all the usefull parameters out self.updateFromPeakList() self.updateSpectrumWindow() self.setWidgetEntries() ########################################################################### # TBD I suspect this is for matching region with peak list, but may be obsolete now def getSpectrumWindowList(self): """ get list of windows which spectrum could be in """ windows = {} if self.params.peakList: views = getSpectrumViews(self.params.peakList.dataSource) for view in views: windows[view.spectrumWindowPane.spectrumWindow] = None return [[w.name, w] for w in windows.keys()] def updateSpectrumWindow(self): """ update the spectrum window """ windowData = self.getSpectrumWindowList() index = -1 names = [] window = self.rootWindow if windowData: names = [x[0] for x in windowData] windows = [x[1] for x in windowData] if window not in windows: window = windows[0] index = windows.index(window) else: window = None windows = [] if window is not self.rootWindow: self.rootWindow = window ########################################################################### # get and set sigma stuff def setSigmaMin(self, dim): value = self.editMinSigmaEntry.get() self._minSigmaHz[dim] = value # dont go and re-write users settings self.customSigma = True # make sure changes are in params object self.updateSigmaParams(dim) self.setWidgetEntries() def getSigmaMin(self, dim): if dim is not None: self.editMinSigmaEntry.set(self._minSigmaHz[dim]) def setSigmaMax(self, dim): value = self.editMaxSigmaEntry.get() self._maxSigmaHz[dim] = value # dont go and re-write users settings self.customSigma = True # make sure changes are in params object self.updateSigmaParams(dim) self.setWidgetEntries() def getSigmaMax(self, dim): if dim is not None: self.editMaxSigmaEntry.set(self._maxSigmaHz[dim]) def updateSigmaParams(self, dim=None): """ updateSigmaParams Just updates the parameters (params obj) for sigma values. If dim is None, do this for each dim """ dataDimRefs = self.params.dataDimRefs if not dataDimRefs: return if not self.params.minSigma or len( self.params.minSigma) != self.params.Ndim: self.params.minSigma = [0.] * self.params.Ndim if not self.params.maxSigma or len( self.params.maxSigma) != self.params.Ndim: self.params.maxSigma = [0.] * self.params.Ndim def updateSigmaParam(dim, dataDimRefs): """ Convert and update sigma for dim """ if self.params.isFreqDim[dim]: # note factor of two! self.params.minSigma[dim] = self.rHz2pnt( self._minSigmaHz[dim], dataDimRefs[dim]) / 2. self.params.maxSigma[dim] = self.rHz2pnt( self._maxSigmaHz[dim], dataDimRefs[dim]) / 2. else: self.params.minSigma[dim] = 1.0 self.params.maxSigma[dim] = 1.0 if dim: updateSigmaParam(dim, dataDimRefs) else: for dim in range(self.params.Ndim): updateSigmaParam(dim, dataDimRefs) # utility functions for sigma values def pnt2rHz(self, point, dataDimRef): """ Point to relative Hz frequency relative to frequency at Zeroeth point Necessary when (for example) looking for width of peak in Hz """ assert point, dataDimRef sigmaBase = pnt2hz(0, dataDimRef) sigmaHz = pnt2hz(point, dataDimRef) return abs(sigmaHz - sigmaBase) def rHz2pnt(self, freq, dataDimRef): """ Relative Hz to point frequency relative to frequency at Zeroeth point Necessary when (for example) looking for width of peak in Hz """ assert freq, dataDimRef sigmaBase = hz2pnt(0, dataDimRef) sigmaPoint = hz2pnt(freq, dataDimRef) return abs(sigmaPoint - sigmaBase) def initSigmaParams(self): """ Set some initial default values for sigma """ self._minSigmaHz = [] self._maxSigmaHz = [] if self.params.Ndim: for dim in range(self.params.Ndim): self._minSigmaHz.append(6.) self._maxSigmaHz.append(28.) ########################################################################### def updateAll(self): self.updateSpectrumWindow() self.updatePeakListList() self.waiting = False def updateAfter(self, obj=None): if self.waiting: return else: self.waiting = True self.after_idle(self.updateAll)
class FilterCloudsPopup(BasePopup): def __init__(self, parent, *args, **kw): self.guiParent = parent self.structure = None self.name = None self.clouds = [] self.rmsds = [] self.names = [] self.atomTypes = None self.waiting = 0 BasePopup.__init__(self, parent=parent, title="Filter Clouds", **kw) def body(self, guiFrame): row = 0 guiFrame.grid_columnconfigure(3, weight=1) label = Label(guiFrame, text='Cloud file names:') label.grid(row=row, column=0, sticky=Tkinter.W) self.fileNameEntry = Entry(guiFrame, text='testHistone\d+.pdb', returnCallback=self.loadClouds) self.fileNameEntry.grid(row=row, column=1, sticky=Tkinter.W) strucLabel = Label(guiFrame, text='Comparison structure') strucLabel.grid(row=row, column=2, sticky=Tkinter.W) self.strucPulldown = PulldownMenu(guiFrame, entries=self.getStructures(), callback=self.setStructure, selected_index=0, do_initial_callback=0) self.strucPulldown.grid(row=row, column=3, sticky=Tkinter.W) row += 1 sdTolLabel = Label(guiFrame, text='Tolerance (SDs):') sdTolLabel.grid(row=row, column=0, sticky=Tkinter.W) self.sdToleranceEntry = FloatEntry(guiFrame, text=2.0, width=6) self.sdToleranceEntry.grid(row=row, column=1, stick=Tkinter.W) atomTypes = ['All', 'H', 'H HA', 'H HA HB'] label = Label(guiFrame, text='RMSD Atom Types:') label.grid(row=row, column=2, sticky=Tkinter.W) self.atomsPulldown = PulldownMenu(guiFrame, entries=atomTypes, callback=self.setAtomTypes, selected_index=0, do_initial_callback=0) self.atomsPulldown.grid(row=row, column=3, sticky=Tkinter.W) row += 1 guiFrame.grid_rowconfigure(row, weight=1) colHeadings = ['#', 'File name', 'RMSD to mean'] self.scrolledMatrix = ScrolledMatrix(guiFrame, initialRows=10, headingList=colHeadings, callback=self.selectCell, objectList=[], textMatrix=[ [], ], multiSelect=1) self.scrolledMatrix.grid(row=row, column=0, columnspan=4, sticky=Tkinter.NSEW) row += 1 texts = [ 'Load\nClouds', 'Align\nClouds', 'Calc\nRMSD', 'Make Cloud\nfrom structure', 'Remove', 'Remove\nbad' ] commands = [ self.loadClouds, self.alignClouds, self.calcRmsd, self.makeStrucCloud, self.deleteClouds, self.filterClouds ] self.bottomButtons = createDismissHelpButtonList( guiFrame, texts=texts, expands=1, commands=commands, help_url=self.help_url) self.bottomButtons.grid(row=row, column=0, columnspan=4, sticky=Tkinter.NSEW) self.update() def alignClouds(self): pattern = self.fileNameEntry.get() self.names = getFileNamesFromPattern(pattern, '.') self.clouds = getCloudsFromFile(self.names, self.guiParent.project) alignClouds(self.clouds, self.names) def loadClouds(self): pattern = self.fileNameEntry.get() self.names = getFileNamesFromPattern(pattern, '.') self.clouds = getCloudsFromFile(self.names, self.guiParent.project) self.name = None self.rmsds = [None for x in range(len(self.clouds))] self.updateAfter() def getStructures(self): names = [ '<None>', ] for molSystem in self.project.sortedMolSystems(): for structure in molSystem.sortedStructureEnsembles(): names.append('%s:%d' % (molSystem.name, structure.ensembleId)) return names def setStructure(self, index, name=None): if index < 1: self.structure = None else: structures = [] for molSystem in self.project.molSystems: for structure in molSystem.structures: structures.append(structure) self.structure = structures[index - 1] self.updateButtons() def setAtomTypes(self, index, name=None): self.atomTypes = atomTypeList[index] def filterClouds(self): if self.clouds: sdTolerance = self.sdToleranceEntry.get() or 2.0 keptClouds = [] meanRmsd = 0.0 N = 0 for rmsd in self.rmsds: meanRmsd += rmsd or 0.0 N += 1 if N > 0: meanRmsd /= float(N) sd = 0.0 for r in self.rmsds: rmsd = r or 0.0 sd += (rmsd - meanRmsd) * (rmsd - meanRmsd) if N > 0: sd /= float(N - 1) sd = sqrt(sd) print meanRmsd, '+/-', sd for i in range(len(self.clouds), 0, -1): rmsd = self.rmsds[i] if abs(rmsd - meanRmsd) > (sdTolerance * sd): self.rmsds.pop(i) self.names.pop(i) self.clouds.pop(i) #print 'Cloud %s is bad' % (cloud) self.updateAfter() def makeStrucCloud(self): if self.structure and self.clouds: pdbFileName = 'CloudForStructure.pdb' atomCoordList = [] atomCoordList0 = [] resDict = {} hmass = 25 resonances = self.clouds[0].keys() resonances2 = [] C = 0 for resonance in resonances: if resonance == 'rmsd': continue resonanceSet = resonance.resonanceSet if resonanceSet: i = list(resonanceSet.resonances).index(resonance) atomSet = list(resonance.resonanceSet.atomSets)[i] coords = getAtomSetCoords(atomSet, self.structure) coord = coords[0] atomCoordList.append([coord.x, coord.y, coord.z]) atomCoordList0.append([coord.x, coord.y, coord.z]) resonances2.append(resonance) C += 1 print len(atomCoordList) print len(resonances), len(resonances2) print "Generating Mean" cloudsList = [] for cloud in self.clouds: orderCloud = [] for resonance in resonances2: x, y, z = cloud.get(resonance) or (0.0, 0.0, 0.0) orderCloud.append([-x, -y, -z]) cloudsList.append(orderCloud) (meanCloud, cloudsList) = alignToMeanCloud(cloudsList) weights = [1.0 for x in atomCoordList] centerCoords(atomCoordList) print "init cen", getMeanCoords(atomCoordList) print "mean cen", getMeanCoords(meanCloud) print "Print aligning struct clouds to mean", len(meanCloud), len( atomCoordList), len(weights) atomCoordsList, error, rotMat = alignCoordinates( meanCloud, atomCoordList, weights) print " Rotation", rotMat writeTypedPdbCloud(atomCoordList, pdbFileName, resonances2) print "Getting centres" oldCentre = getMeanCoords(atomCoordList0) newCentre = getMeanCoords(atomCoordList) delta = [ newCentre[i] - oldCentre[i] for i in range(len(oldCentre)) ] print " New centre", newCentre print " Old centre", oldCentre print " Delta", delta #inverseRot = inverseMatrix(rotMat) model = self.structure.findFirstModel() coordinates = model.coordinates offset = 0 iis = (0, 1, 2) for atom in self.structure.orderedAtoms: next = offset + 3 coords = [coordinates[offset + ii] + delta[ii] for ii in iis] coords = matrixVecMultiply(rotMat, coords) coordinates[offset:next] = coords offset = next model.setSubmatrixData('coordinates', coordinates) clouds = getCloudsFromFile([ pdbFileName, ], self.structure.root) self.clouds.append(clouds[0]) self.rmsds.append(None) self.names.append(pdbFileName) self.updateAfter() def calcRmsd(self): if self.clouds: if len(self.scrolledMatrix.currentObjects) < 2: clouds = self.clouds else: clouds = [] for name in self.scrolledMatrix.currentObjects: clouds.append(self.clouds[self.names.index(name)]) self.rmsds = filterClouds(clouds, atomTypes=self.atomTypes) self.updateAfter() def deleteClouds(self): if self.names and self.name and showOkCancel( 'Confirm', 'Really remove selected clouds?'): indices = [] for name in self.scrolledMatrix.currentObjects: i = self.names.index(name) indices.append(i) indices.sort() indices.reverse() for i in indices: self.clouds.pop(i) self.rmsds.pop(i) self.names.pop(i) self.name = None self.updateAfter() def selectCell(self, name, row, col): self.name = name self.updateButtons() def updateAfter(self, *opt): if self.waiting: return else: self.waiting = 1 self.after_idle(self.update) def destroy(self): BasePopup.destroy(self) def updateButtons(self): if self.names: self.bottomButtons.buttons[1].enable() self.bottomButtons.buttons[2].enable() self.bottomButtons.buttons[5].enable() else: self.bottomButtons.buttons[1].disable() self.bottomButtons.buttons[2].enable() self.bottomButtons.buttons[5].disable() if self.name: self.bottomButtons.buttons[4].enable() else: self.bottomButtons.buttons[4].disable() if self.structure and self.clouds: self.bottomButtons.buttons[3].enable() else: self.bottomButtons.buttons[3].disable() def update(self): textMatrix = [] objectList = self.names self.updateButtons() i = 0 for name in objectList: datum = [] datum.append(i + 1) datum.append(name) datum.append(self.rmsds[i]) textMatrix.append(datum) i += 1 if not objectList: textMatrix = [ [], ] self.scrolledMatrix.update(objectList=objectList, textMatrix=textMatrix) self.waiting = 0
class CloudHomologueAssignPopup(BasePopup): def __init__(self, parent, *args, **kw): self.guiParent = parent self.project = parent.getProject() self.molSystem = None self.chain = None self.assignment = None self.scores = [] BasePopup.__init__(self, parent, title="Cloud Threader", **kw) def body(self, guiFrame): guiFrame.grid_columnconfigure(3, weight=1) row = 0 label = Label(guiFrame, text='Molecular system: ') label.grid(row=row, column=0, sticky=Tkinter.NW) self.molSysPulldown = PulldownMenu(guiFrame, self.changeMolSystem, selected_index=-1, do_initial_callback=0) self.molSysPulldown.grid(row=row, column=1, sticky=Tkinter.NW) label = Label(guiFrame, text='Clouds files: ') label.grid(row=row, column=2, sticky=Tkinter.NW) self.filenameEntry = Entry(guiFrame,text='perfect00.pdb') self.filenameEntry.grid(row=row, column=3, sticky=Tkinter.NW) row += 1 label = Label(guiFrame, text='Chain: ') label.grid(row=row, column=0, sticky=Tkinter.NW) self.chainPulldown = PulldownMenu(guiFrame, self.changeChain, selected_index=-1, do_initial_callback=0) self.chainPulldown.grid(row=row, column=1, sticky=Tkinter.NW) label = Label(guiFrame, text='Thread steps: ') label.grid(row=row, column=2, sticky=Tkinter.NW) self.numStepsEntry = IntEntry(guiFrame,text=3000) self.numStepsEntry.grid(row=row, column=3, sticky=Tkinter.NW) row += 1 label = Label(guiFrame, text='Homologue PDB file: ') label.grid(row=row, column=0, sticky=Tkinter.NW) self.pdbEntry = Entry(guiFrame,text='') self.pdbEntry.grid(row=row, column=1, sticky=Tkinter.NW) label = Label(guiFrame, text='Dist. Threshold: ') label.grid(row=row, column=2, sticky=Tkinter.NW) self.distEntry = FloatEntry(guiFrame,text=3.0) self.distEntry.grid(row=row, column=3, sticky=Tkinter.NW) row += 1 label = Label(guiFrame, text='Global score: ') label.grid(row=row, column=0, sticky=Tkinter.NW) self.globalScoreLabel = Label(guiFrame, text='') self.globalScoreLabel.grid(row=row, column=1, sticky=Tkinter.NW) label = Label(guiFrame, text='Assignment Threshold: ') label.grid(row=row, column=2, sticky=Tkinter.NW) self.thresholdEntry = FloatEntry(guiFrame,text=-4.5) self.thresholdEntry.grid(row=row, column=3, sticky=Tkinter.NW) row += 1 guiFrame.grid_rowconfigure(row, weight=1) self.graph = ScrolledGraph(guiFrame, width=300, height=200) self.graph.grid(row=row, column=0, columnspan=4, sticky = Tkinter.NSEW) row += 1 texts = ['Run','Assign!'] commands = [self.run, self.assignSpinSystems] bottomButtons = createDismissHelpButtonList(guiFrame,texts=texts,commands=commands,expands=0,help_url=None) bottomButtons.grid(row=row, column=0, columnspan=4, sticky=Tkinter.EW) self.assignButton = bottomButtons.buttons[1] for func in ('__init__','delete'): Implementation.registerNotify(self.updateMolSystems, 'ccp.molecule.MolSystem.MolSystem', func) Implementation.registerNotify(self.updateChains, 'ccp.molecule.MolSystem.Chain', func) self.updateMolSystems() self.updateChains() def update(self): if self.assignment and self.scores: self.assignButton.enable() else: self.assignButton.disable() def run(self): if self.chain: pattern = self.filenameEntry.get() nSteps = self.numStepsEntry.get() or 4000 pdbFile = self.pdbEntry.get() dist = self.distEntry.get() or 3.0 pgb = ProgressBar(self, text='Searching', total=nSteps) files = getFileNamesFromPattern(pattern , '.') if not files: return clouds = getCloudsFromFile(files, self.chain.root) score, self.scores, self.assignment = cloudHomologueAssign(self.chain, clouds, pdbFile, dist, nSteps, self.graph, pgb) pgb.destroy() self.globalScoreLabel.set(str(score)) self.update() def assignSpinSystems(self): if self.assignment and self.scores: if showWarning('Query','Are you sure?'): threshold = self.thresholdEntry.get() or -4.0 i = 0 for residue in self.assignment.keys(): if self.scores[residue] > threshold: spinSystem = self.assignment[residue] assignSpinSystemResidue(spinSystem,residue=None) for residue in self.assignment.keys(): if self.scores[residue] > threshold: i += 1 spinSystem = self.assignment[residue] assignSpinSystemResidue(spinSystem,residue=residue) showWarning('Done','%d residues assigned' % i) def getMolSystems(self): names = [] for molSystem in self.project.molSystems: if molSystem.chains: names.append( '%s' % (molSystem.code) ) return names def changeMolSystem(self, i, name): self.molSystem = self.project.findFirstMolSystem(code=name) def updateMolSystems(self, *opt): names = self.getMolSystems() if names: if not self.molSystem: self.molSystem = self.project.findFirstMolSystem(code=names[0]) self.molSysPulldown.setup(names, names.index(self.molSystem.code)) def getChains(self): chains = [] if self.molSystem: for chain in self.molSystem.chains: chains.append( [chain.code, chain] ) return chains def changeChain(self, i, name=None): if not name: i = self.chainPulldown.selected_index chains = self.getChains() if chains: self.chain = chains[i][1] def updateChains(self, *chain): chains = self.getChains() if chains: names = [x[0] for x in chains] if (not self.chain) or (self.chain.code not in names): self.chain = chains[0][1] self.chainPulldown.setup(names, names.index(self.chain.code) ) self.update() def destroy(self): for func in ('__init__','delete'): Implementation.unregisterNotify(self.updateMolSystems, 'ccp.molecule.MolSystem.MolSystem', func) Implementation.unregisterNotify(self.updateChains, 'ccp.molecule.MolSystem.Chain', func) BasePopup.destroy(self)
class MidgePopup(BasePopup): def __init__(self, parent, *args, **kw): self.guiParent = parent self.project = parent.getProject() self.waiting = 0 self.specFreq = 800.13 self.maxIter = 15 self.mixTime = 60 self.corrTime = 11.5 self.leakRate = 2.0 self.ratioHD = 0.9 self.peakListDict = {} self.peakListDict3d = {} self.noesyPeakList = None self.noesy3dPeakList = None self.carbonLabel = 0 self.nitrogenLabel = 1 self.noesyPeakList1 = None self.noesyPeakList2 = None self.noesyPeakList3 = None self.noesyPeakList3d = None self.resonances = None self.noesyPeaks = None self.distanceConstraintList = None self.antiDistConstraintList = None self.adcAtomTypes = None self.structure = None BasePopup.__init__(self, parent, title="Relaxation Matrix Optimisation", **kw) def body(self, guiFrame): self.specFreqEntry = IntEntry(self, text=self.specFreq, width=8, returnCallback=self.setSpecFreq) self.maxIterEntry = IntEntry(self, text=self.maxIter, width=8, returnCallback=self.setMaxIter) self.mixTimeEntry = FloatEntry(self, text=self.mixTime, width=8, returnCallback=self.setMixTime) self.corrTimeEntry = FloatEntry(self, text=self.corrTime, width=8, returnCallback=self.setCorrTime) self.leakRateEntry = FloatEntry(self, text=self.leakRate, width=8, returnCallback=self.setLeakRate) guiFrame.grid_columnconfigure(0, weight=1) guiFrame.grid_rowconfigure(1, weight=1) row = 0 labelFrame0 = LabelFrame(guiFrame, text='Input data') labelFrame0.grid(row=row, column=0, sticky=Tkinter.NSEW) labelFrame0.grid_columnconfigure(3, weight=1) label = Label(labelFrame0, text='Assigned NOESY spectrum') label.grid(row=0, column=0, sticky=Tkinter.NW) self.noesyPulldown = PulldownMenu(labelFrame0, entries=self.getNoesys(), callback=self.setNoesy, selected_index=0, do_initial_callback=0) self.noesyPulldown.grid(row=0, column=1, sticky=Tkinter.NW) label = Label(labelFrame0, text='H/D ratio: ') label.grid(row=0, column=2, sticky=Tkinter.NW) self.ratioHDEntry = FloatEntry(labelFrame0, text=self.ratioHD, width=6) self.ratioHDEntry.grid(row=0, column=3, sticky=Tkinter.NW) label = Label(labelFrame0, text='NOESY spectrum 1:') label.grid(row=1, column=0, sticky=Tkinter.NW) self.tmix1Pulldown = PulldownMenu(labelFrame0, entries=self.getNoesys(), callback=self.setNoesy1, selected_index=-0, do_initial_callback=0) self.tmix1Pulldown.grid(row=1, column=1, sticky=Tkinter.NW) label = Label(labelFrame0, text='Tmix (ms): ') label.grid(row=1, column=2, sticky=Tkinter.NW) self.tmix1Entry = FloatEntry(labelFrame0, text=60, width=6) self.tmix1Entry.grid(row=1, column=3, sticky=Tkinter.NW) label = Label(labelFrame0, text='NOESY spectrum 2:') label.grid(row=2, column=0, sticky=Tkinter.NW) self.tmix2Pulldown = PulldownMenu(labelFrame0, entries=self.getNoesys(), callback=self.setNoesy2, selected_index=0, do_initial_callback=0) self.tmix2Pulldown.grid(row=2, column=1, sticky=Tkinter.NW) label = Label(labelFrame0, text='Tmix (ms): ') label.grid(row=2, column=2, sticky=Tkinter.NW) self.tmix2Entry = FloatEntry(labelFrame0, text=120, width=6) self.tmix2Entry.grid(row=2, column=3, sticky=Tkinter.NW) label = Label(labelFrame0, text='NOESY spectrum 3:') label.grid(row=3, column=0, sticky=Tkinter.NW) self.tmix3Pulldown = PulldownMenu(labelFrame0, entries=self.getNoesys(), callback=self.setNoesy3, selected_index=0, do_initial_callback=0) self.tmix3Pulldown.grid(row=3, column=1, sticky=Tkinter.NW) label = Label(labelFrame0, text='Tmix (ms): ') label.grid(row=3, column=2, sticky=Tkinter.NW) self.tmix3Entry = FloatEntry(labelFrame0, text=200, width=6) self.tmix3Entry.grid(row=3, column=3, sticky=Tkinter.NW) label = Label(labelFrame0, text='3D NOESY:') label.grid(row=4, column=0, sticky=Tkinter.NW) self.noesy3dPulldown = PulldownMenu(labelFrame0, entries=self.getNoesys3d(), callback=self.setNoesy3d, selected_index=0, do_initial_callback=0) self.noesy3dPulldown.grid(row=4, column=1, sticky=Tkinter.NW) label10 = Label(labelFrame0, text='Num peaks:') label10.grid(row=5, column=0, sticky=Tkinter.NW) self.numPeaksLabel = Label(labelFrame0, text='0') self.numPeaksLabel.grid(row=5, column=1, sticky=Tkinter.NW) label11 = Label(labelFrame0, text='Num resonances:') label11.grid(row=5, column=2, sticky=Tkinter.NW) self.numResonancesLabel = Label(labelFrame0, text='0') self.numResonancesLabel.grid(row=5, column=3, sticky=Tkinter.NW) row += 1 labelFrame1 = LabelFrame(guiFrame, text='Parameters') labelFrame1.grid(row=row, column=0, sticky=Tkinter.NSEW) labelFrame1.grid_columnconfigure(3, weight=1) label = Label(labelFrame1, text='15N labelled sample:') label.grid(row=0, column=0, sticky=Tkinter.NW) self.nitrogenSelect = CheckButton(labelFrame1, callback=self.setNitrogenLabel) self.nitrogenSelect.grid(row=0, column=1, sticky=Tkinter.W) self.nitrogenSelect.set(1) label = Label(labelFrame1, text='13C labelled sample:') label.grid(row=0, column=2, sticky=Tkinter.NW) self.carbonSelect = CheckButton(labelFrame1, callback=self.setCarbonLabel) self.carbonSelect.grid(row=0, column=3, sticky=Tkinter.W) self.carbonSelect.set(0) labelFrame1.grid_rowconfigure(1, weight=1) data = [ self.specFreq, self.maxIter, self.mixTime, self.corrTime, self.leakRate ] colHeadings = [ 'Spectrometer\nfrequency', 'Max\niterations', 'Mixing\ntime (ms)', 'Correl.\ntime (ns)', 'Leak\nrate' ] editWidgets = [ self.specFreqEntry, self.maxIterEntry, self.mixTimeEntry, self.corrTimeEntry, self.leakRateEntry, ] editGetCallbacks = [ self.getSpecFreq, self.getMaxIter, self.getMixTime, self.getCorrTime, self.getLeakRate, ] editSetCallbacks = [ self.setSpecFreq, self.setMaxIter, self.setMixTime, self.setCorrTime, self.setLeakRate, ] self.midgeParamsMatrix = ScrolledMatrix( labelFrame1, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, maxRows=1, initialCols=5, headingList=colHeadings, callback=None, objectList=[ 'None', ], textMatrix=[ data, ]) self.midgeParamsMatrix.grid(row=1, column=0, columnspan=4, sticky=Tkinter.NSEW) label10 = Label(labelFrame1, text='Benchmark structure') label10.grid(row=2, column=0, sticky=Tkinter.NW) self.structurePulldown = PulldownMenu(labelFrame1, entries=self.getStructures(), callback=self.setStructure, selected_index=0, do_initial_callback=0) self.structurePulldown.grid(row=2, column=1, sticky=Tkinter.NW) label11 = Label(labelFrame1, text='ADC atom types:') label11.grid(row=2, column=2, sticky=Tkinter.NW) self.adcAtomsPulldown = PulldownMenu(labelFrame1, entries=self.getAdcAtomTypes(), callback=self.setAdcAtomTypes, selected_index=0, do_initial_callback=0) self.adcAtomsPulldown.grid(row=2, column=3, sticky=Tkinter.NW) row += 1 labelFrame2 = LabelFrame(guiFrame, text='Output') labelFrame2.grid(row=row, column=0, sticky=Tkinter.NSEW) labelFrame2.grid_columnconfigure(3, weight=1) label20 = Label(labelFrame2, text='Distance constraints:') label20.grid(row=0, column=0, sticky=Tkinter.NW) self.distConstrLabel = Label(labelFrame2, text='0') self.distConstrLabel.grid(row=0, column=1, sticky=Tkinter.NW) label21 = Label(labelFrame2, text='Anti-distance constraints:') label21.grid(row=0, column=2, sticky=Tkinter.NW) self.antiConstrLabel = Label(labelFrame2, text='0') self.antiConstrLabel.grid(row=0, column=3, sticky=Tkinter.NW) texts = [ 'Calculate distances', 'Show distance\nconstraints', 'Show anti-distance\nconstraints' ] commands = [ self.calculateDistances, self.showConstraints, self.showAntiConstraints ] self.midgeButtons = ButtonList(labelFrame2, expands=1, texts=texts, commands=commands) self.midgeButtons.grid(row=1, column=0, columnspan=4, sticky=Tkinter.NSEW) row += 1 self.bottomButtons = createDismissHelpButtonList(guiFrame, expands=0, help_url=None) self.bottomButtons.grid(row=row, column=0, columnspan=4, sticky=Tkinter.EW) self.getPeaks() self.getResonances() self.update() self.geometry('600x400') def setCarbonLabel(self, boolean): self.carbonLabel = boolean def setNitrogenLabel(self, boolean): self.nitrogenLabel = boolean def update(self): if self.resonances and ( (self.noesyPeaks and self.noesyPeakList1 and self.noesyPeakList2 and self.noesyPeakList3) or self.noesyPeakList3d): self.midgeButtons.buttons[0].enable() else: self.midgeButtons.buttons[0].disable() if self.distanceConstraintList: self.distConstrLabel.set( str(len(self.distanceConstraintList.constraints))) self.midgeButtons.buttons[1].enable() else: self.distConstrLabel.set('') self.midgeButtons.buttons[1].disable() if self.antiDistConstraintList: self.antiConstrLabel.set( str(len(self.antiDistConstraintList.constraints))) self.midgeButtons.buttons[2].enable() else: self.antiConstrLabel.set('') self.midgeButtons.buttons[2].disable() if self.resonances: self.numResonancesLabel.set(str(len(self.resonances))) else: self.numResonancesLabel.set('') if self.noesyPeaks: self.numPeaksLabel.set(str(len(self.noesyPeaks))) else: self.numPeaksLabel.set('') def getStructures(self): names = [ '<None>', ] for molSystem in self.project.sortedMolSystems(): for structure in molSystem.sortedStructureEnsembles(): names.append('%s:%d' % (molSystem.name, structure.ensembleId)) return names def setStructure(self, index, name=None): if index < 1: self.structure = None else: structures = [] for molSystem in self.project.molSystems: for structure in molSystem.structureEnsembles: structures.append(structure) self.structure = structures[index - 1] def getAdcAtomTypes(self): return ['<None>', 'HN', 'HN HA', 'HN HA HB'] def setAdcAtomTypes(self, index, name=None): if name is None: name = self.adcAtomsPulldown.getSelected() if name == '<None>': name = None self.adcAtomTypes = name def getResonances(self): resonanceDict = {} if self.noesyPeaks: for peak in self.noesyPeaks: for peakDim in peak.peakDims: for contrib in peakDim.peakDimContribs: resonanceDict[contrib.resonance] = 1 # TBD: Set resonance.name for typing self.resonances = resonanceDict.keys() def getPeaks(self): if self.noesyPeakList: self.noesyPeaks = self.noesyPeakList.sortedPeaks() def calculateDistances(self): resonances = list(self.resonances) resDict = {} for resonance in resonances: resDict[resonance.serial] = resonance ratioHD = self.ratioHDEntry.get() or self.ratioHD tmix1 = self.tmix1Entry.get() or 60 tmix2 = self.tmix2Entry.get() or 120 tmix3 = self.tmix3Entry.get() or 200 data = [(tmix1, self.noesyPeakList1), (tmix2, self.noesyPeakList2), (tmix3, self.noesyPeakList3)] data.sort() mixingTimes = [x[0] for x in data] peakLists = [x[1] for x in data] # get a clean, symmetric and normalised NOE matrix noeMatrix = getNoeMatrixFromPeaks(self.noesyPeaks, resonances, peakLists, mixingTimes, ratioHD=ratioHD, analysis=self.guiParent) # optimiseRelaxation will remove unconstrained resonances self.distanceConstraintList, resonances = optimiseRelaxation( resonances, noeMatrix, self.mixTime, self.specFreq, self.corrTime, self.leakRate, self.carbonLabel, self.nitrogenLabel, maxIter=self.maxIter) #constrainSpinSystems(self.distanceConstraintList) # for testing calculate distances from structure overrides any resonances: uses assigned ones #(self.distanceConstraintList, self.resonances) = self.cheatForTesting() #self.antiDistConstraintList = self.distanceConstraintList protonNumbs = {'CH3': 3, 'Haro': 2, 'HN': 1, 'H': 1} PI = 3.1415926535897931 GH = 2.6752e4 HB = 1.05459e-27 CONST = GH * GH * GH * GH * HB * HB tc = 1.0e-9 * self.corrTime wh = 2.0 * PI * self.specFreq * 1.0e6 j0 = CONST * tc j1 = CONST * tc / (1.0 + wh * wh * tc * tc) j2 = CONST * tc / (1.0 + 4.0 * wh * wh * tc * tc) #jself = 6.0*j2 + 3.0*j1 + j0 jcross = 6.0 * j2 - j0 if self.distanceConstraintList and self.noesyPeakList: constraintHead = self.distanceConstraintList.nmrConstraintStore if self.adcAtomTypes: adcDict = { 'HN': ['H'], 'HN HA': ['H', 'HA', 'HA1', 'HA2'], 'HN HA HB': ['H', 'HA', 'HA1', 'HA2', 'HB', 'HB2', 'HB3'] } allowedAtomTypes = adcDict[self.adcAtomTypes] print "Making ADCs" self.antiDistConstraintList = makeNoeAdcs( resonances[:], self.noesyPeakList.dataSource, constraintHead, allowedAtomTypes=allowedAtomTypes) print "Done ADCs" if self.structure: N = len(self.resonances) sigmas = [[] for i in range(N)] for i in range(N): sigmas[i] = [0.0 for j in range(N)] for constraint in self.distanceConstraintList.constraints: item = constraint.findFirstItem() resonances = list(item.resonances) ri = resDict[resonances[0].resonanceSerial] rj = resDict[resonances[1].resonanceSerial] i = self.resonances.index(ri) j = self.resonances.index(rj) atomSets1 = list(ri.resonanceSet.atomSets) atomSets2 = list(rj.resonanceSet.atomSets) if atomSets1 == atomSets2: ass = list(atomSets1) atomSets1 = [ ass[0], ] atomSets2 = [ ass[-1], ] distance = getAtomSetsDistance(atomSets1, atomSets2, self.structure) r = distance * 1e-8 nhs = protonNumbs[rj.name] sigma = 0.1 * jcross * nhs / (r**6) sigmas[i][j] = sigma constraint.setOrigData(distance) self.update() def showConstraints(self): if self.distanceConstraintList: self.guiParent.browseConstraints( constraintList=self.distanceConstraintList) def showAntiConstraints(self): if self.antiDistConstraintList: self.guiParent.browseConstraints( constraintList=self.antiDistConstraintList) def getNoesys3d(self): peakLists = getThroughSpacePeakLists(self.project) names = [ '<None>', ] for peakList in peakLists: spectrum = peakList.dataSource if spectrum.numDim != 3: continue name = '%s:%s:%s' % (spectrum.experiment.name, spectrum.name, peakList.serial) names.append(name) self.peakListDict3d[name] = peakList if not self.noesyPeakList: self.noesyPeakList = peakList return names def getNoesys(self): peakLists = getThroughSpacePeakLists(self.project) names = [ '<None>', ] for peakList in peakLists: spectrum = peakList.dataSource name = '%s:%s:%s' % (spectrum.experiment.name, spectrum.name, peakList.serial) names.append(name) self.peakListDict[name] = peakList if not self.noesyPeakList: self.noesyPeakList = peakList return names def setNoesy(self, index, name=None): if not name: name = self.noesyPulldown.getSelected() if name == '<None>': self.noesyPeakList = None else: self.noesyPeakList = self.peakListDict[name] self.getPeaks() self.getResonances() self.update() def setNoesy1(self, index, name=None): if not name: name = self.tmix1Pulldown.getSelected() if name != '<None>': self.noesyPeakList1 = self.peakListDict[name] else: self.noesyPeakList1 = None self.update() def setNoesy2(self, index, name=None): if not name: name = self.tmix2Pulldown.getSelected() if name != '<None>': self.noesyPeakList2 = self.peakListDict[name] else: self.noesyPeakList2 = None self.update() def setNoesy3(self, index, name=None): if not name: name = self.tmix3Pulldown.getSelected() if name != '<None>': self.noesyPeakList3 = self.peakListDict[name] else: self.noesyPeakList3 = None self.update() def setNoesy3d(self, index, name=None): if not name: name = self.noesy3dPulldown.getSelected() if name != '<None>': self.noesyPeakList3d = self.peakListDict3d[name] self.noesyPeaks = self.noesyPeakList3d.sortedPeaks() else: self.noesyPeakList3d = None self.noesyPeaks = [] self.getResonances() self.update() def updateMidgeParams(self): data = [ self.specFreq, self.maxIter, self.mixTime, self.corrTime, self.leakRate ] self.midgeParamsMatrix.update(textMatrix=[ data, ]) def getSpecFreq(self, obj): self.specFreqEntry.set(self.specFreq) def getMaxIter(self, obj): self.maxIterEntry.set(self.maxIter) def getMixTime(self, obj): self.mixTimeEntry.set(self.mixTime) def getCorrTime(self, obj): self.corrTimeEntry.set(self.corrTime) def getLeakRate(self, obj): self.leakRateEntry.set(self.leakRate) def setSpecFreq(self, event): value = self.specFreqEntry.get() if value is not None: self.specFreq = value self.updateMidgeParams() def setMaxIter(self, event): value = self.maxIterEntry.get() if value is not None: self.maxIter = value self.updateMidgeParams() def setMixTime(self, event): value = self.mixTimeEntry.get() if value is not None: self.mixTime = value self.updateMidgeParams() def setCorrTime(self, event): value = self.corrTimeEntry.get() if value is not None: self.corrTime = value self.updateMidgeParams() def setLeakRate(self, event): value = self.leakRateEntry.get() if value is not None: self.leakRate = value self.updateMidgeParams() def destroy(self): BasePopup.destroy(self)
class CloudsPopup(BasePopup): def __init__(self, parent, *args, **kw): self.guiParent = parent self.project = parent.getProject() self.waiting = 0 self.specFreq = 800.13 self.maxIter = 50 self.mixTime = 60 self.corrTime = 11.5 self.leakRate = 2.0 self.peakListDict = {} self.noesyPeakList = None self.tocsyPeakList = None self.noesy3dPeakList = None self.hsqcPeakList = None self.maxIntens = 37000000 self.resonances = None self.origResonances = None self.noesyPeaks = None self.distanceConstraintList = None self.antiDistConstraintList = None self.numClouds = 100 self.filePrefix = 'cloud_' self.cloudsFiles = [] self.adcAtomTypes = 'HN' self.structure = None # step num, initial temp, final temp, cooling steps, MD steps, MD tau, rep scale self.coolingScheme = [] self.coolingScheme.append([1, 1, 1, 3, 500, 0.001, 0]) self.coolingScheme.append([2, 80000, 4000, 19, 1000, 0.001, 0]) self.coolingScheme.append([3, 4000, 1, 5, 500, 0.001, 0]) self.coolingScheme.append([4, 15000, 1, 3, 1000, 0.001, 0]) self.coolingScheme.append([5, 1, 1, 5, 500, 0.001, 0]) self.coolingScheme.append([6, 8000, 1, 3, 1000, 0.001, 0]) self.coolingScheme.append([7, 1, 1, 5, 500, 0.001, 0]) self.coolingScheme.append([8, 3000, 25, 60, 2500, 0.001, 1]) self.coolingScheme.append([9, 25, 25, 1, 7500, 0.001, 1]) self.coolingScheme.append([10, 10, 10, 1, 7500, 0.001, 1]) self.coolingScheme.append([11, 0.01, 0.01, 1, 7500, 0.0005, 1]) self.coolingStep = None BasePopup.__init__(self, parent, title="Resonance Clouds Analysis", **kw) def body(self, guiFrame): self.specFreqEntry = IntEntry(self, text=self.specFreq, width=8, returnCallback=self.setSpecFreq) self.maxIterEntry = IntEntry(self, text=self.maxIter, width=8, returnCallback=self.setMaxIter) self.mixTimeEntry = FloatEntry(self, text=self.mixTime, width=8, returnCallback=self.setMixTime) self.corrTimeEntry = FloatEntry(self, text=self.corrTime, width=8, returnCallback=self.setCorrTime) self.leakRateEntry = FloatEntry(self, text=self.leakRate, width=8, returnCallback=self.setLeakRate) self.maxIntensEntry = IntEntry(self, text=self.maxIntens, width=8, returnCallback=self.setMaxIntens) self.mdInitTempEntry = FloatEntry(self, text='', returnCallback=self.setMdInitTemp) self.mdFinTempEntry = FloatEntry(self, text='', returnCallback=self.setMdFinTemp) self.mdCoolStepsEntry = IntEntry(self, text='', returnCallback=self.setMdCoolSteps) self.mdSimStepsEntry = IntEntry(self, text='', returnCallback=self.setMdSimSteps) self.mdTauEntry = FloatEntry(self, text='', returnCallback=self.setMdTau) self.mdRepScaleEntry = FloatEntry(self, text='', returnCallback=self.setMdRepScale) guiFrame.grid_columnconfigure(0, weight=1) row = 0 frame0 = LabelFrame(guiFrame, text='Setup peak lists') frame0.grid(row=row, column=0, sticky=Tkinter.NSEW) frame0.grid(row=row, column=0, sticky=Tkinter.NSEW) frame0.grid_columnconfigure(1, weight=1) f0row = 0 label00 = Label(frame0, text='1H-1H NOESY spectrum') label00.grid(row=f0row, column=0, sticky=Tkinter.NW) self.noesyPulldown = PulldownMenu(frame0, entries=self.getNoesys(), callback=self.setNoesy, selected_index=0, do_initial_callback=0) self.noesyPulldown.grid(row=f0row, column=1, sticky=Tkinter.NW) f0row += 1 label01 = Label(frame0, text='15N HSQC spectrum') label01.grid(row=f0row, column=0, sticky=Tkinter.NW) self.hsqcPulldown = PulldownMenu(frame0, entries=self.getHsqcs(), callback=self.setHsqc, selected_index=0, do_initial_callback=0) self.hsqcPulldown.grid(row=f0row, column=1, sticky=Tkinter.NW) f0row += 1 label02 = Label(frame0, text='15N HSQC TOCSY spectrum') label02.grid(row=f0row, column=0, sticky=Tkinter.NW) self.tocsyPulldown = PulldownMenu(frame0, entries=self.getTocsys(), callback=self.setTocsy, selected_index=0, do_initial_callback=0) self.tocsyPulldown.grid(row=f0row, column=1, sticky=Tkinter.NW) f0row += 1 label02 = Label(frame0, text='15N HSQC NOESY spectrum') label02.grid(row=f0row, column=0, sticky=Tkinter.NW) self.noesy3dPulldown = PulldownMenu(frame0, entries=self.getNoesy3ds(), callback=self.setNoesy3d, selected_index=0, do_initial_callback=0) self.noesy3dPulldown.grid(row=f0row, column=1, sticky=Tkinter.NW) f0row += 1 texts = ['Setup resonances & peaks', 'Show Peaks', 'Show resonances'] commands = [self.setupResonances, self.showPeaks, self.showResonances] self.setupButtons = ButtonList(frame0, expands=1, texts=texts, commands=commands) self.setupButtons.grid(row=f0row, column=0, columnspan=2, sticky=Tkinter.NSEW) f0row += 1 self.label03a = Label(frame0, text='Resonances found: 0') self.label03a.grid(row=f0row, column=0, sticky=Tkinter.NW) self.label03b = Label(frame0, text='NOESY peaks found: 0') self.label03b.grid(row=f0row, column=1, sticky=Tkinter.NW) row += 1 frame1 = LabelFrame(guiFrame, text='Calculate distance constraints') frame1.grid(row=row, column=0, sticky=Tkinter.NSEW) frame1.grid_columnconfigure(3, weight=1) f1row = 0 frame1.grid_rowconfigure(f1row, weight=1) data = [ self.specFreq, self.maxIter, self.mixTime, self.corrTime, self.leakRate, self.maxIntens ] colHeadings = [ 'Spectrometer\nfrequency', 'Max\niterations', 'Mixing\ntime (ms)', 'Correl.\ntime (ns)', 'Leak\nrate', 'Max\nintensity' ] editWidgets = [ self.specFreqEntry, self.maxIterEntry, self.mixTimeEntry, self.corrTimeEntry, self.leakRateEntry, self.maxIntensEntry, ] editGetCallbacks = [ self.getSpecFreq, self.getMaxIter, self.getMixTime, self.getCorrTime, self.getLeakRate, self.getMaxIntens, ] editSetCallbacks = [ self.setSpecFreq, self.setMaxIter, self.setMixTime, self.setCorrTime, self.setLeakRate, self.setMaxIntens, ] self.midgeParamsMatrix = ScrolledMatrix( frame1, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, maxRows=1, initialCols=5, headingList=colHeadings, callback=None, objectList=[ 'None', ], textMatrix=[ data, ]) self.midgeParamsMatrix.grid(row=f1row, column=0, columnspan=4, sticky=Tkinter.NSEW) f1row += 1 label10 = Label(frame1, text='Benchmark structure') label10.grid(row=f1row, column=0, sticky=Tkinter.NW) self.structurePulldown = PulldownMenu(frame1, entries=self.getStructures(), callback=self.setStructure, selected_index=0, do_initial_callback=0) self.structurePulldown.grid(row=f1row, column=1, sticky=Tkinter.NW) label11 = Label(frame1, text='ADC atom types:') label11.grid(row=f1row, column=2, sticky=Tkinter.NW) self.adcAtomsPulldown = PulldownMenu(frame1, entries=self.getAdcAtomTypes(), callback=self.setAdcAtomTypes, selected_index=0, do_initial_callback=0) self.adcAtomsPulldown.grid(row=f1row, column=3, sticky=Tkinter.NW) f1row += 1 texts = [ 'Calculate distances', 'Show distance\nconstraints', 'Show anti-distance\nconstraints' ] commands = [ self.calculateDistances, self.showConstraints, self.showAntiConstraints ] self.midgeButtons = ButtonList(frame1, expands=1, texts=texts, commands=commands) self.midgeButtons.grid(row=f1row, column=0, columnspan=4, sticky=Tkinter.NSEW) f1row += 1 self.distConstrLabel = Label(frame1, text='Distance constraints:') self.distConstrLabel.grid(row=f1row, column=0, columnspan=2, sticky=Tkinter.NW) self.antiConstrLabel = Label(frame1, text='Anti-distance constraints:') self.antiConstrLabel.grid(row=f1row, column=2, columnspan=2, sticky=Tkinter.NW) row += 1 guiFrame.grid_rowconfigure(row, weight=1) frame2 = LabelFrame(guiFrame, text='Proton cloud molecular dynamics') frame2.grid(row=row, column=0, sticky=Tkinter.NSEW) frame2.grid_columnconfigure(1, weight=1) f2row = 0 frame2.grid_rowconfigure(f2row, weight=1) data = [ self.specFreq, self.maxIter, self.mixTime, self.corrTime, self.leakRate ] colHeadings = [ 'Step', 'Initial temp.', 'Final temp.', 'Cooling steps', 'MD steps', 'MD tau', 'Rep. scale' ] editWidgets = [ None, self.mdInitTempEntry, self.mdFinTempEntry, self.mdCoolStepsEntry, self.mdSimStepsEntry, self.mdTauEntry, self.mdRepScaleEntry ] editGetCallbacks = [ None, self.getMdInitTemp, self.getMdFinTemp, self.getMdCoolSteps, self.getMdSimSteps, self.getMdTau, self.getMdRepScale ] editSetCallbacks = [ None, self.setMdInitTemp, self.setMdFinTemp, self.setMdCoolSteps, self.setMdSimSteps, self.setMdTau, self.setMdRepScale ] self.coolingSchemeMatrix = ScrolledMatrix( frame2, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, maxRows=9, initialRows=12, headingList=colHeadings, callback=self.selectCoolingStep, objectList=self.coolingScheme, textMatrix=self.coolingScheme) self.coolingSchemeMatrix.grid(row=f2row, column=0, columnspan=4, sticky=Tkinter.NSEW) f2row += 1 texts = ['Move earlier', 'Move later', 'Add step', 'Remove step'] commands = [ self.moveStepEarlier, self.moveStepLater, self.addCoolingStep, self.removeCoolingStep ] self.coolingSchemeButtons = ButtonList(frame2, expands=1, commands=commands, texts=texts) self.coolingSchemeButtons.grid(row=f2row, column=0, columnspan=4, sticky=Tkinter.EW) f2row += 1 label20 = Label(frame2, text='Number of clouds:') label20.grid(row=f2row, column=0, sticky=Tkinter.NW) self.numCloudsEntry = FloatEntry(frame2, text=100, returnCallback=self.setNumClouds, width=10) self.numCloudsEntry.grid(row=f2row, column=1, sticky=Tkinter.NW) label21 = Label(frame2, text='Cloud file prefix:') label21.grid(row=f2row, column=2, sticky=Tkinter.NW) self.filePrefixEntry = Entry(frame2, text='cloud_', returnCallback=self.setFilePrefix, width=10) self.filePrefixEntry.grid(row=f2row, column=3, sticky=Tkinter.NW) f2row += 1 texts = ['Start molecular dynamics', 'Show dynamics progress'] commands = [self.startMd, self.showMdProgress] self.mdButtons = ButtonList(frame2, expands=1, commands=commands, texts=texts) self.mdButtons.grid(row=f2row, column=0, columnspan=4, sticky=Tkinter.NSEW) row += 1 self.bottomButtons = createDismissHelpButtonList(guiFrame, expands=0, help_url=None) self.bottomButtons.grid(row=row, column=0, sticky=Tkinter.EW) self.setButtonStates() def getStructures(self): names = [ '<None>', ] for molSystem in self.project.sortedMolSystems(): for structure in molSystem.sortedStructureEnsembles(): names.append('%s:%d' % (molSystem.name, structure.ensembleId)) return names def setStructure(self, index, name=None): if index < 1: self.structure = None else: structures = [] for molSystem in self.project.molSystems: for structure in molSystem.structureEnsembles: structures.append(structure) self.structure = structures[index - 1] def getAdcAtomTypes(self): return ['HN', 'HN HA', 'HN HA HB'] def setAdcAtomTypes(self, index, name=None): if name is None: name = self.adcAtomsPulldown.getSelected() self.adcAtomTypes = name def startMd(self): self.setNumClouds() self.setFilePrefix() if (self.distanceConstraintList and self.antiDistConstraintList and (self.numClouds > 0) and self.filePrefix): resDict = {} for resonance in self.guiParent.project.currentNmrProject.resonances: resDict[resonance.serial] = resonance resonances = [] for constraint in self.distanceConstraintList.constraints: for item in constraint.items: for fixedResonance in item.resonances: if resDict.get( fixedResonance.resonanceSerial) is not None: resonances.append( resDict[fixedResonance.resonanceSerial]) resDict[fixedResonance.resonanceSerial] = None startMdProcess(self.numClouds, self.distanceConstraintList, resonances, self.coolingScheme, self.filePrefix) #structGen = self.distanceConstraintList.structureGeneration serials = [] for resonance in resonances: serials.append(resonance.serial) clouds = [] for i in range(self.numClouds): clouds.append('%s%3.3d.pdb' % (self.filePrefix, i)) self.guiParent.application.setValues( self.distanceConstraintList.nmrConstraintStore, 'clouds', values=clouds) self.guiParent.application.setValues( self.distanceConstraintList.nmrConstraintStore, 'cloudsResonances', values=serials) # do better than this check for creation def showMdProgress(self): n = 0 m = self.numClouds for i in range(m): pdbFileName = '%s%3.3d.pdb' % (self.filePrefix, i) if os.path.exists(pdbFileName): n += 1 p = n * 100 / float(m) text = 'Done %d of %d clouds (%1.2f)%%' % (n, m, p) showInfo('MD Progress', text) def setFilePrefix(self, text=None): if not text: text = self.filePrefixEntry.get() if text: self.filePrefix = text def setNumClouds(self, n=None, *event): if not n: n = self.numCloudsEntry.get() if n: self.numClouds = int(n) def calculateDistances(self): # setup normalisation factor intensityMax # self.maxIter # what if failure ? resDict = {} for resonance in self.project.currentNmrProject.resonances: resDict[resonance.serial] = resonance self.resonances = self.origResonances intensityFactors = [1.0 for x in range(len(self.resonances))] # optimiseRelaxation will remove unconstrained resonances self.distanceConstraintList = optimiseRelaxation( self.resonances, self.noesyPeaks, intensityMax=self.maxIntens, intensityFactors=intensityFactors, tmix=self.mixTime, sf=self.specFreq, tcor=self.corrTime, rleak=self.leakRate) constrainSpinSystems(self.distanceConstraintList) # for testing calculate distances from structure overrides any resonances: uses assigned ones #(self.distanceConstraintList, self.resonances) = self.cheatForTesting() #self.antiDistConstraintList = self.distanceConstraintList protonNumbs = {'CH3': 3, 'Haro': 2, 'HN': 1, 'H': 1} PI = 3.1415926535897931 GH = 2.6752e4 HB = 1.05459e-27 CONST = GH * GH * GH * GH * HB * HB tc = 1.0e-9 * self.corrTime wh = 2.0 * PI * self.specFreq * 1.0e6 j0 = CONST * tc j1 = CONST * tc / (1.0 + wh * wh * tc * tc) j2 = CONST * tc / (1.0 + 4.0 * wh * wh * tc * tc) #jself = 6.0*j2 + 3.0*j1 + j0 jcross = 6.0 * j2 - j0 if self.distanceConstraintList: constraintStore = self.distanceConstraintList.nmrConstraintStore dict = { 'HN': ['H'], 'HN HA': ['H', 'HA', 'HA1', 'HA2'], 'HN HA HB': ['H', 'HA', 'HA1', 'HA2', 'HB', 'HB2', 'HB3'] } self.antiDistConstraintList = makeNoeAdcs( self.resonances, self.noesyPeakList.dataSource, constraintStore, allowedAtomTypes=dict[self.adcAtomTypes]) if self.structure: N = len(self.resonances) sigmas = [[] for i in range(N)] for i in range(N): sigmas[i] = [0.0 for j in range(N)] for constraint in self.distanceConstraintList.constraints: resonances = list(constraint, findFirstItem().resonances) ri = resDict[resonances[0].resonanceSerial] rj = resDict[resonances[1].resonanceSerial] i = self.resonances.index(ri) j = self.resonances.index(rj) atomSets1 = list(ri.resonanceSet.atomSets) atomSets2 = list(rj.resonanceSet.atomSets) if atomSets1 == atomSets2: ass = list(atomSets1) atomSets1 = [ ass[0], ] atomSets2 = [ ass[-1], ] distance = getAtomSetsDistance(atomSets1, atomSets2, self.structure) r = distance * 1e-8 nhs = protonNumbs[rj.name] sigma = 0.1 * jcross * nhs / (r**6) sigmas[i][j] = sigma constraint.setDetails('Known Dist: %4.3f' % (distance)) #for constraint in self.antiDistConstraintList.constraints: # atomSets1 = list(resonances[0].resonanceSet.atomSets) # atomSets2 = list(resonances[1].resonanceSet.atomSets) # distance = getAtomSetsDistance(atomSets1, atomSets2, self.structure) # constraint.setDetails('Known Dist: %4.3f' % (distance)) fp = open('sigmas.out', 'w') for i in range(N - 1): for j in range(i + 1, N): if sigmas[i][j] != 0.0: fp.write('%3.1d %3.1d %9.2e\n' % (i, j, sigmas[i][j])) #fp.write('\n') fp.close() self.setButtonStates() def cheatForTesting(self, atomSelection='H'): """ Makes a perfect cloud from a structure. """ project = self.project structure = self.guiParent.argumentServer.getStructure() constraintStore = makeNmrConstraintStore(project) distConstraintList = NmrConstraint.DistanceConstraintList( constraintStore) chain = structure.findFirstCoodChain() structureGeneration.hydrogenResonances = [] molSystem = structure.molSystem atomSets = [] resonances = [] i = 0 for resonance in project.currentNmrProject.resonances: if resonance.isotopeCode == '1H': if resonance.resonanceSet: atomSet = resonance.resonanceSet.findFirstAtomSet() atom = atomSet.findFirstAtom() seqId = atom.residue.seqId if (seqId < 9) or (seqId > 78): continue if atom.residue.chain.molSystem is molSystem: if atomSelection == 'methyl': if len(atomSet.atoms) == 3: if atom.residue.ccpCode not in ('Ala', 'Val', 'Ile', 'Leu', 'Thr', 'Met'): continue elif atom.name != 'H': continue elif atomSelection == 'amide': if atom.name != 'H': continue if atom.name == 'H': resonance.name = 'HN' else: resonance.name = 'H' resonances.append(resonance) atomSets.append(list(resonance.resonanceSet.atomSets)) i += 1 print "Found %d atomSets" % (len(atomSets)) weight = 1 adcWeight = 1 constrDict = {} N = len(atomSets) for i in range(N - 1): atomSets0 = atomSets[i] residue0 = atomSets0[0].findFirstAtom().residue.seqId print "R", residue0 for j in range(i + 1, N): if j == i: continue atomSets1 = atomSets[j] dist = getAtomSetsDistance(atomSets0, atomSets1, structure) if not dist: continue if dist < 5.5: fixedResonance0 = getFixedResonance( constraintStore, resonances[i]) fixedResonance1 = getFixedResonance( constraintStore, resonances[j]) constrDict[i] = 1 constrDict[j] = 1 constraint = NmrConstraint.DistanceConstraint( distConstraintList, weight=weight, targetValue=dist, upperLimit=dist + (dist / 10), lowerLimit=dist - (dist / 10), error=dist / 5) item = NmrConstraint.DistanceConstraintItem( constraint, resonances=[fixedResonance0, fixedResonance1]) elif (atomSets1[0].findFirstAtom().name == 'H') and (atomSets0[0].findFirstAtom().name == 'H') and (dist > 7): #else: fixedResonance0 = getFixedResonance( constraintStore, resonances[i]) fixedResonance1 = getFixedResonance( constraintStore, resonances[j]) constrDict[i] = 1 constrDict[j] = 1 constraint = NmrConstraint.DistanceConstraint( distConstraintList, weight=adcWeight, targetValue=75, upperLimit=175, lowerLimit=5.0, error=94.5) item = NmrConstraint.DistanceConstraintItem( constraint, resonances=[fixedResonance0, fixedResonance1]) return (distConstraintList, resonances) def showConstraints(self): if self.distanceConstraintList: self.guiParent.browseConstraints( constraintList=self.distanceConstraintList) def showAntiConstraints(self): if self.antiDistConstraintList: self.guiParent.browseConstraints( constraintList=self.antiDistConstraintList) def showPeaks(self): self.guiParent.viewPeaks(peaks=self.noesyPeaks) def showResonances(self): pass #self.guiParent.viewResonances(resonances=self.resonances) def setupResonances(self): if self.noesyPeakList and self.noesy3dPeakList and self.tocsyPeakList and self.hsqcPeakList: disambiguateNoesyPeaks(self.noesyPeakList, self.noesy3dPeakList, self.tocsyPeakList, self.hsqcPeakList) (self.origResonances, self.noesyPeaks, null) = getCloudsResonanceList(self.guiParent.argumentServer, hsqcPeakList=self.hsqcPeakList, tocsy3dPeakList=self.tocsyPeakList, noesy2dPeakList=self.noesyPeakList) self.setButtonStates() def setButtonStates(self): if self.origResonances: self.label03a.set('Resonances found: %d' % (len(self.origResonances))) if self.noesyPeaks: self.label03b.set('NOESY peaks found: %d' % (len(self.noesyPeaks))) if self.noesyPeakList and self.tocsyPeakList and self.hsqcPeakList: self.setupButtons.buttons[0].enable() else: self.setupButtons.buttons[0].disable() if self.noesyPeaks: self.setupButtons.buttons[1].enable() else: self.setupButtons.buttons[1].disable() if self.origResonances: self.setupButtons.buttons[2].enable() else: self.setupButtons.buttons[2].disable() if self.noesyPeaks and self.origResonances: self.midgeButtons.buttons[0].enable() else: self.midgeButtons.buttons[0].disable() if self.distanceConstraintList: self.midgeButtons.buttons[1].enable() self.distConstrLabel.set( 'Distance constraints: %d' % len(self.distanceConstraintList.constraints)) else: self.distConstrLabel.set('Distance constraints:') self.midgeButtons.buttons[1].disable() if self.antiDistConstraintList: self.antiConstrLabel.set( 'Anti-distance constraints: %d' % len(self.antiDistConstraintList.constraints)) self.midgeButtons.buttons[2].enable() else: self.antiConstrLabel.set('Anti-distance constraints:') self.midgeButtons.buttons[2].disable() if (self.distanceConstraintList and self.antiDistConstraintList and (self.numClouds > 0) and self.filePrefix): self.mdButtons.buttons[0].enable() self.mdButtons.buttons[1].enable() else: self.mdButtons.buttons[0].disable() self.mdButtons.buttons[1].disable() def getNoesys(self): names = [] spectra = getSpectraByType(self.project, '2dNOESY') for spectrum in spectra: for peakList in spectrum.peakLists: name = '%s:%s:%s' % (spectrum.experiment.name, spectrum.name, peakList.serial) names.append(name) self.peakListDict[name] = peakList if not self.noesyPeakList: self.noesyPeakList = peakList return names def setNoesy(self, index, name=None): if not name: name = self.noesyPulldown.getSelected() self.noesyPeakList = self.peakListDict[name] self.setButtonStates() def getTocsys(self): names = [] spectra = getSpectraByType(self.project, '3dTOCSY') for spectrum in spectra: for peakList in spectrum.peakLists: name = '%s:%s:%s' % (spectrum.experiment.name, spectrum.name, peakList.serial) names.append(name) self.peakListDict[name] = peakList if not self.tocsyPeakList: self.tocsyPeakList = peakList return names def getNoesy3ds(self): names = [] spectra = getSpectraByType(self.project, '3dNOESY') for spectrum in spectra: for peakList in spectrum.peakLists: name = '%s:%s:%s' % (spectrum.experiment.name, spectrum.name, peakList.serial) names.append(name) self.peakListDict[name] = peakList if not self.noesy3dPeakList: self.noesy3dPeakList = peakList return names def setTocsy(self, index, name=None): if not name: name = self.tocsyPulldown.getSelected() self.tocsyPeakList = self.peakListDict[name] self.setButtonStates() def setNoesy3d(self, index, name=None): if not name: name = self.noesy3dPulldown.getSelected() self.noesy3dPeakList = self.peakListDict[name] self.setButtonStates() def getHsqcs(self): names = [] spectra = getSpectraByType(self.project, 'HSQC') for spectrum in spectra: for peakList in spectrum.peakLists: name = '%s:%s:%s' % (spectrum.experiment.name, spectrum.name, peakList.serial) names.append(name) self.peakListDict[name] = peakList if not self.hsqcPeakList: self.hsqcPeakList = peakList return names def setHsqc(self, index, name=None): if not name: name = self.hsqcPulldown.getSelected() self.hsqcPeakList = self.peakListDict[name] self.setButtonStates() def getMdInitTemp(self, coolingStep): self.mdInitTempEntry.set(coolingStep[1]) def getMdFinTemp(self, coolingStep): self.mdFinTempEntry.set(coolingStep[2]) def getMdCoolSteps(self, coolingStep): self.mdCoolStepsEntry.set(coolingStep[3]) def getMdSimSteps(self, coolingStep): self.mdSimStepsEntry.set(coolingStep[4]) def getMdTau(self, coolingStep): self.mdTauEntry.set(coolingStep[5]) def getMdRepScale(self, coolingStep): self.mdRepScaleEntry.set(coolingStep[6]) def setMdInitTemp(self, event): value = self.mdInitTempEntry.get() if value is not None: self.coolingStep[1] = value self.updateCoolingScheme() def setMdFinTemp(self, event): value = self.mdFinTempEntry.get() if value is not None: self.coolingStep[2] = value self.updateCoolingScheme() def setMdCoolSteps(self, event): value = self.mdCoolStepsEntry.get() if value is not None: self.coolingStep[3] = value self.updateCoolingScheme() def setMdSimSteps(self, event): value = self.mdSimStepsEntry.get() if value is not None: self.coolingStep[4] = value self.updateCoolingScheme() def setMdTau(self, event): value = self.mdTauEntry.get() if value is not None: self.coolingStep[5] = value self.updateCoolingScheme() def setMdRepScale(self, event): value = self.mdRepScaleEntry.get() if value is not None: self.coolingStep[6] = value self.updateCoolingScheme() def selectCoolingStep(self, object, row, col): self.coolingStep = object def moveStepEarlier(self): if self.coolingStep: i = self.coolingStep[0] - 1 if i > 0: coolingStep = self.coolingScheme[i - 1] coolingStep[0] = i + 1 self.coolingStep[0] = i self.coolingScheme[i - 1] = self.coolingStep self.coolingScheme[i] = coolingStep self.updateCoolingScheme() self.coolingSchemeMatrix.hilightObject(self.coolingStep) def moveStepLater(self): if self.coolingStep: i = self.coolingStep[0] - 1 if i < len(self.coolingScheme) - 1: coolingStep = self.coolingScheme[i + 1] coolingStep[0] = i + 1 self.coolingStep[0] = i + 2 self.coolingScheme[i + 1] = self.coolingStep self.coolingScheme[i] = coolingStep self.updateCoolingScheme() self.coolingSchemeMatrix.hilightObject(self.coolingStep) def addCoolingStep(self): i = len(self.coolingScheme) + 1 datum = [i, 3000, 100, 10, 2500, 0.001, 1] self.coolingScheme.append(datum) self.updateCoolingScheme() def removeCoolingStep(self): if self.coolingStep: coolingScheme = [] i = 0 for coolingStep in self.coolingScheme: if coolingStep is not self.coolingStep: i += 1 coolingStep[0] = i coolingScheme.append(coolingStep) self.coolingScheme = coolingScheme self.updateCoolingScheme() def updateCoolingScheme(self): objectList = self.coolingScheme textMatrix = self.coolingScheme self.coolingSchemeMatrix.update(objectList=objectList, textMatrix=textMatrix) def updateMidgeParams(self): data = [ self.specFreq, self.maxIter, self.mixTime, self.corrTime, self.leakRate, self.maxIntens ] self.midgeParamsMatrix.update(textMatrix=[ data, ]) def getSpecFreq(self, obj): self.specFreqEntry.set(self.specFreq) def getMaxIter(self, obj): self.maxIterEntry.set(self.maxIter) def getMixTime(self, obj): self.mixTimeEntry.set(self.mixTime) def getCorrTime(self, obj): self.corrTimeEntry.set(self.corrTime) def getLeakRate(self, obj): self.leakRateEntry.set(self.leakRate) def getMaxIntens(self, obj): self.maxIntensEntry.set(self.maxIntens) def setSpecFreq(self, event): value = self.specFreqEntry.get() if value is not None: self.specFreq = value self.updateMidgeParams() def setMaxIter(self, event): value = self.maxIterEntry.get() if value is not None: self.maxIter = value self.updateMidgeParams() def setMixTime(self, event): value = self.mixTimeEntry.get() if value is not None: self.mixTime = value self.updateMidgeParams() def setCorrTime(self, event): value = self.corrTimeEntry.get() if value is not None: self.corrTime = value self.updateMidgeParams() def setLeakRate(self, event): value = self.leakRateEntry.get() if value is not None: self.leakRate = value self.updateMidgeParams() def setMaxIntens(self, event): value = self.maxIntensEntry.get() if value is not None: self.maxIntens = value self.updateMidgeParams() def destroy(self): BasePopup.destroy(self)
class DangleFrame(Frame): def __init__(self, parent, dangleGui, project=None, *args, **kw): self.guiParent = parent self.dangleGui = dangleGui self.dangleDir = None self.dangleChain = None self.dangleResidue = None #self.outDir = OUTDIR self.row = None self.col = None self.project = project self.nmrProject = None self.colorScheme = 'red' self.chain = None self.shiftList = None self.dangleStore = False # Not None self.constraintSet = None self.ensemble = None Frame.__init__(self, parent=parent) self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(1, weight=1) row = 0 # TOP LEFT FRAME frame = LabelFrame(self, text='Options') frame.grid(row=row, column=0, sticky='nsew') frame.columnconfigure(5, weight=1) label = Label(frame, text='Chain') label.grid(row=0, column=0, sticky='w') self.chainPulldown = PulldownList( frame, callback=self.changeChain, tipText='Choose the molecular system chain to make predictions for' ) self.chainPulldown.grid(row=0, column=1, sticky='w') label = Label(frame, text='Shift List') label.grid(row=0, column=2, sticky='w') self.shiftListPulldown = PulldownList( frame, callback=self.changeShiftList, tipText='Select the shift list to take input chemical shifts from') self.shiftListPulldown.grid(row=0, column=3, sticky='w') label = Label(frame, text='Max No. of Islands:') label.grid(row=0, column=4, sticky='w') sizes = range(10) texts = [str(s) for s in sizes] + [ 'Do not reject', ] self.rejectPulldown = PulldownList( frame, texts=texts, objects=sizes + [ None, ], tipText= 'Select the maximum allowed number of disontinuous prediction islands' ) self.rejectPulldown.set(DEFAULT_MAX_ISLANDS) # Actual value not index self.rejectPulldown.grid(row=0, column=5, sticky='w') label = Label(frame, text='Dangle Run:') label.grid(row=1, column=0, sticky='w') self.dangleStorePulldown = PulldownList( frame, callback=self.changeDangleStore, tipText='Select a run number to store DANGLE results within') self.dangleStorePulldown.grid(row=1, column=1, sticky='w') label = Label(frame, text='Ensemble:') label.grid(row=1, column=2, sticky='w') self.ensemblePulldown = PulldownList( frame, callback=self.changeEnsemble, tipText= 'Select the structure ensemble for superimposition of angle values on the GLE plots' ) self.ensemblePulldown.grid(row=1, column=3, sticky='w') label = Label(frame, text='Restraint Set:') label.grid(row=1, column=4, sticky='w') self.constrSetPulldown = PulldownList( frame, callback=self.changeConstraintSet, tipText= 'Select the CCPN restraint set to store DANGLE dihedral angle restraints in' ) self.constrSetPulldown.grid(row=1, column=5, sticky='w') # TOP RIGHT FRAME outerFrame = Frame(self) outerFrame.grid(row=row, column=1, rowspan=2, sticky='nsew') outerFrame.rowconfigure(0, weight=1) outerFrame.columnconfigure(0, weight=1) frame = LabelFrame(outerFrame, text='Global Likelihood Estimates') frame.grid(row=0, column=0, sticky='nsew') frame.rowconfigure(1, weight=1) frame.columnconfigure(2, weight=1) self.prevPlot = ViewRamachandranFrame(frame, relief='sunken', defaultPlot=False, width=180, height=180, bgColor=self.cget('bg'), nullColor='#000000', titleText='Previous', xTicks=False, yTicks=False, xLabel='', yLabel='', showCoords=False) self.prevPlot.grid(row=0, column=0, sticky='nsew') self.prevPlot.getPlotColor = self.getPlotColor self.nextPlot = ViewRamachandranFrame(frame, relief='sunken', defaultPlot=False, width=180, height=180, bgColor=self.cget('bg'), nullColor='#000000', titleText='Next', xTicks=False, yTicks=False, xLabel='', yLabel='', showCoords=False) self.nextPlot.grid(row=0, column=1, sticky='nsew') self.nextPlot.getPlotColor = self.getPlotColor self.plot = ViewRamachandranFrame(frame, relief='sunken', defaultPlot=False, width=360, height=360, bgColor=self.cget('bg'), nullColor='#000000') self.plot.grid(row=1, column=0, columnspan=2, sticky='nsew') self.plot.selectColor = '#FFB0B0' self.plot.getPlotColor = self.getPlotColor # BOTTOM RIGHT FRAME frame = Frame(outerFrame) frame.grid(row=1, column=0, sticky='nsew') frame.rowconfigure(0, weight=1) frame.columnconfigure(0, weight=1) texts = ('Previous', ' Next ') commands = (self.showPrevious, self.showNext) tipTexts = [ 'Show GLE plot of angle predictions for previous residue in chain', 'Show GLE plot of angle predictions for next residue in chain' ] buttonList = ButtonList(frame, texts, commands, tipTexts=tipTexts) buttonList.grid(row=0, column=0, sticky='nsew') row += 1 # BOTTOM LEFT FRAME frame = LabelFrame(self, text='Dihedral Angle Predictions') frame.grid(row=row, column=0, sticky='nsew') frame.grid_columnconfigure(0, weight=1) frame.grid_rowconfigure(0, weight=1) self.floatEntry = FloatEntry(self, text='', returnCallback=self.setFloatEntry, width=10, formatPlaces=9) tipTexts = [ 'Residue number in chain', 'Residue type code', 'Number of high scoring discontinuous angle predictions', 'Predicted secondary structure code', 'Predicted phi dihedral angle (CO-N-CA-CO)', 'Predicted psi dihedral angle (N-CA-CO-N)', 'Upper bound of phi angle prediction', 'Lower bound of phi angle prediction', 'Upper bound of psi angle prediction', 'Lower bound of phi angle prediction', 'Chemical shifts used in prediction' ] headingList = [ 'Res\nNum', 'Res\nType', 'No. of\nIslands', 'SS', 'Phi', 'Psi', 'Phi\nUpper', 'Phi\nLower', 'Psi\nUpper', 'Psi\nLower', 'Chemical Shifts' ] editWidgets = [ None, None, None, None, self.floatEntry, self.floatEntry, self.floatEntry, self.floatEntry, self.floatEntry, self.floatEntry ] editGetCallbacks = [ None, None, None, None, self.getFloatEntry, self.getFloatEntry, self.getFloatEntry, self.getFloatEntry, self.getFloatEntry, self.getFloatEntry ] editSetCallbacks = [ None, None, None, None, self.setFloatEntry, self.setFloatEntry, self.setFloatEntry, self.setFloatEntry, self.setFloatEntry, self.setFloatEntry ] self.predictionMatrix = ScrolledMatrix( frame, headingList=headingList, multiSelect=True, callback=self.selectCell, tipTexts=tipTexts, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks) # doubleCallback=self.loadGLEs) self.predictionMatrix.grid(row=0, column=0, sticky='nsew') row += 1 tipTexts = [ 'Remove the predictions for the selected residues', 'Run the DANGLE method to predict dihedral angles and secondary structure', 'Delete the DANGLE results stored under the current run number', 'Store the angle predictions and bounds in a new CCPN dihedral angle restraint list', 'Store the secondary structure predictions in the CCPN project' ] texts = [ 'Clear\nSelected', 'Run Prediction!', 'Delete\nCurrent Run', 'Commit\nRestraints', 'Commit\nSecondary Structure' ] commands = [ self.clearSelected, self.runDangle, self.deleteRun, self.storeDihedralConstraints, self.storeSecondaryStructure ] self.buttonList = createDismissHelpButtonList( self, texts=texts, commands=commands, # dismiss_text='Quit', dismiss_cmd=self.dangleGui.quit, help_url=self.dangleGui.help_url, expands=True, tipTexts=tipTexts) self.buttonList.grid(row=row, column=0, columnspan=2, sticky='ew') self.buttonList.buttons[1].config(bg='#C0FFFF') self.updateProject(project) self.notify(dangleGui.registerNotify) def destroy(self): self.notify(self.dangleGui.unregisterNotify) Frame.destroy(self) def notify(self, notifyfunc): for func in ('__init__', 'delete'): notifyfunc(self.updateChainPulldown, 'ccp.molecule.MolSystem.Chain', func) for func in ('__init__', 'delete', 'setName'): notifyfunc(self.updateShiftListPulldown, 'ccp.nmr.Nmr.ShiftList', func) for func in ('__init__', 'delete'): notifyfunc(self.updateConstrSetPulldown, 'ccp.nmr.NmrConstraint.NmrConstraintStore', func) for func in ('__init__', 'delete'): notifyfunc(self.updateEnsemblePulldown, 'ccp.molecule.MolStructure.StructureEnsemble', func) def updateProject(self, project): if project: self.project = project self.nmrProject = project.currentNmrProject or self.project.newNmrProject( name=project.name) self.updateShiftListPulldown() self.updateChainPulldown() self.updateDangleStorePulldown() self.updateConstrSetPulldown() self.updateEnsemblePulldown() self.updatePredictionMatrixAfter() def makeDangleInput(self, filename, chain, shiftList): if (not chain) or (not shiftList): return residues = chain.sortedResidues() seq = '' for residue in residues: if residue.molResidue.chemComp.code1Letter: seq += residue.molResidue.chemComp.code1Letter else: seq += 'X' res_0 = residues[0].seqId fopen = open(filename, 'w') fopen.write('<entry>\n') fopen.write('\t<res_0>%d</res_0>\n' % res_0) fopen.write('\t<seq_1>%s</seq_1>\n' % seq) fopen.write('\t<chain>%s</chain>\n' % chain.code) fopen.write('\t<cs_data>\n') fopen.write('\t\t<!-- res_id res_name atom_name chemical_shift -->') numShift = 0 for residue in residues: for atom in residue.atoms: atomSet = atom.getAtomSet() shifts = getAtomSetShifts(atomSet, shiftList=shiftList) if (len(shifts) == 0): continue # to average ambiguous chemical shifts ??????????????????????????? value = 0. for shift in shifts: value += shift.value value = value / float(len(shifts)) at_name = atom.name res_name = residue.ccpCode res_num = residue.seqId fopen.write('\n\t\t%5s\t%s\t%-4s\t%.3f' % (res_num, res_name, at_name, value)) numShift += 1 fopen.write('\n\t</cs_data>\n') fopen.write('</entry>\n') fopen.close() return numShift def deleteRun(self): if self.dangleStore: msg = 'Really delete DANGLE run "%s"?' % self.dangleStore.name if showOkCancel('Confirm', msg, parent=self): self.dangleStore.delete() self.dangleStore = None self.dangleChain = None self.updatePredictionMatrix() self.updateDangleStorePulldown() def runDangle(self): chain = self.chain shiftList = self.shiftList if (not chain) or (not shiftList): showError('Cannot Run DANGLE', 'Please specify a chain and a shift list.', parent=self) return # check if there is a DangleChain available self.checkDangleStore() dangleStore = self.dangleStore if not dangleStore: return dangleChain = dangleStore.findFirstDangleChain(chain=chain) if dangleChain: data = (chain.code, dangleChain.shiftList.serial) msg = 'Predictions for Chain %s using Shift List %d already exist.\nReplace data?' % data if not showYesNo('Replace Data', msg, parent=self): return else: self.dangleChain = dangleChain dangleChain.shiftList = shiftList else: self.dangleChain = dangleStore.newDangleChain(chain=chain, shiftList=shiftList) #dangleStore.packageLocator.repositories[0].url.dataLocation = '/home/msc51/ccpn/NexusTestGI' #dangleStore.packageName = 'cambridge.dangle' repository = dangleStore.packageLocator.repositories[0] array = dangleStore.packageName.split('.') path = os.path.join(repository.url.dataLocation, *array) path = os.path.join(path, dangleStore.name, chain.code) # Dangle_dir/dangleStoreName/chainCode if not os.path.exists(path): os.makedirs(path) self.dangleDir = path inputFile = os.path.join(self.dangleDir, 'dangle_cs.inp') if os.path.isfile(inputFile): os.unlink(inputFile) outputFile = os.path.join(self.dangleDir, 'danglePred.txt') if os.path.isfile(outputFile): os.unlink(outputFile) numShift = self.makeDangleInput(inputFile, chain, shiftList) if not os.path.isfile(inputFile): msg = 'No DANGLE input has been generated.\nPlease check shift lists.' showError('File Does Not Exist', msg, parent=self) return if numShift == 0: msg = 'No shift data in input file.\nPerhaps shifts are not assigned.\nContinue prediction anyway?' if not showYesNo('Empty DANGLE input', msg, parent=self): return rejectThresh = self.rejectPulldown.getObject() # Use the Reference info from the main installation # location must be absolute because DANGLE could be run from anywhere location = os.path.dirname(dangleModule.__file__) progressBar = ProgressBar(self) self.update_idletasks() dangle = Dangle(location, inputFile=inputFile, outputDir=self.dangleDir, reject=rejectThresh, angleOnly=False, progressBar=progressBar, writePgm=False) #self.dangleDir = '/home/msc51/nexus/gItest/DanglePred/' #outputFile = '/home/msc51/nexus/gItest/DanglePred/danglePred.txt' predictions = dangle.predictor.predictions gleScores = dangle.predictor.gleScores self.readPredictions(predictions, gleScores) self.updatePredictionMatrix() def readPredictions(self, predictions, gleScores): progressBar = ProgressBar(self, text='Reading DANGLE predictions') progressBar.total = len(predictions) - 2 # 2 header lines residues = self.dangleChain.chain.sortedResidues() getDangleResidue = self.dangleChain.findFirstDangleResidue newDangleResidue = self.dangleChain.newDangleResidue for residue in residues: seqId = residue.seqId prediction = predictions.get(seqId) if prediction is None: continue gleMatrix = gleScores[seqId] progressBar.increment() #resNum, resName = prediction[:2]; numIsland = prediction[2] ss = prediction[10] angles = [min(179.9999, a) for a in prediction[3:10]] phi, phiUpper, phiLower, psi, psiUpper, psiLower, omega = angles # Normalise to max maxVal = max(gleMatrix) gleMatrix = [ max(0, int(val / maxVal * 65535)) / 65535.0 for val in gleMatrix ] dangleResidue = getDangleResidue(residue=residue) if not dangleResidue: dangleResidue = newDangleResidue( phiPsiLikelihoodMatrix=gleMatrix, residue=residue) else: dangleResidue.phiPsiLikelihoodMatrix = gleMatrix dangleResidue.numIslands = numIsland dangleResidue.phiValue = phi dangleResidue.phiUpper = phiUpper dangleResidue.phiLower = phiLower dangleResidue.psiValue = psi dangleResidue.psiUpper = psiUpper dangleResidue.psiLower = psiLower dangleResidue.omegaValue = omega dangleResidue.secStrucCode = ss progressBar.destroy() def readPredictionFile(self, filename, chain): try: fopen = open(filename, 'r') except: showError('File Reading Error', 'DANGLE prediction file %s cannot be open.' % filename, parent=self) return lines = fopen.readlines() progressBar = ProgressBar(self, text='Reading DANGLE predictions') progressBar.total = len(lines) - 2 # 2 header lines lines = lines[2:] for line in lines: progressBar.increment() if (line == '\n'): continue array = line.split() # keep everything as string resNum = int(array[0]) resName = array[1] numIsland = int(array[2]) phi = array[3] phiUpper = array[4] phiLower = array[5] psi = array[6] psiUpper = array[7] psiLower = array[8] omega = array[9] ss = array[10] if (phi == 'None'): phi = None else: phi = float(phi) if (psi == 'None'): psi = None else: psi = float(psi) if (omega == 'None'): omega = None else: omega = float(omega) if omega == 180: omega = 179.9 if (phiUpper == 'None'): phiUpper = None else: phiUpper = float(phiUpper) if (phiLower == 'None'): phiLower = None else: phiLower = float(phiLower) if (psiUpper == 'None'): psiUpper = None else: psiUpper = float(psiUpper) if (psiLower == 'None'): psiLower = None else: psiLower = float(psiLower) if (ss == 'None'): ss = None path = os.path.join(self.dangleDir, 'Res_%d.pgm' % resNum) gleMatrix = self.readGLE(path) residue = chain.findFirstResidue(seqId=int(resNum)) dangleResidue = self.dangleChain.findFirstDangleResidue( residue=residue) if not dangleResidue: dangleResidue = self.dangleChain.newDangleResidue( phiPsiLikelihoodMatrix=gleMatrix, residue=residue) else: dangleResidue.phiPsiLikelihoodMatrix = gleMatrix dangleResidue.numIslands = numIsland dangleResidue.phiValue = phi dangleResidue.phiUpper = phiUpper dangleResidue.phiLower = phiLower dangleResidue.psiValue = psi dangleResidue.psiUpper = psiUpper dangleResidue.psiLower = psiLower dangleResidue.omegaValue = omega dangleResidue.secStrucCode = ss # Delete temp pgm files to save space once data is in CCPN os.unlink(path) fopen.close() progressBar.destroy() def readGLE(self, gleFile): if not os.path.isfile(gleFile): msg = 'No scorogram Res_%d.pgm\nin directory %s.' % ( resNum, self.dangleDir) showError('File Reading Error', msg, parent=self) return None fopen = open(gleFile, 'r') lines = fopen.readlines() dims = lines[2].split() lines = lines[4:] fopen.close() # only read the top left corner of a 10X10 square bin # all readings in the same bin are identical binSize = 10 matrix = [] for j in range(36): x = j * binSize * binSize * 36 for i in range(36): y = i * binSize v = int(lines[x + y].strip()) matrix.append(v) maxVal = float(max(matrix)) for i in range(len(matrix)): matrix[i] = matrix[i] / maxVal return matrix def getPhiPsiPredictions(self): #if self.dangleChain: # dResidues = self.dangleChain.dangleResidues dResidues = self.predictionMatrix.objectList phiData = [] psiData = [] for dResidue in dResidues: resNum = dResidue.residue.seqCode phi = dResidue.phiValue psi = dResidue.psiValue phiData.append((resNum, phi)) psiData.append((resNum, psi)) return (phiData, psiData) def clearSelected(self): for dangleResidue in self.predictionMatrix.currentObjects: dangleResidue.numIslands = None dangleResidue.phiValue = None dangleResidue.psiValue = None dangleResidue.omegaValue = None dangleResidue.phiUpper = None dangleResidue.phiLower = None dangleResidue.psiUpper = None dangleResidue.psiLower = None dangleResidue.secStrucCode = None self.updatePredictionMatrixAfter() def storeSecondaryStructure(self): if not self.dangleChain: return getSpinSystem = self.nmrProject.findFirstResonanceGroup newSpinSystem = self.nmrProject.newResonanceGroup n = 0 for dangleResidue in self.dangleChain.dangleResidues: ssCode = dangleResidue.secStrucCode if not ssCode: continue residue = dangleResidue.residue if not residue: continue spinSystem = getSpinSystem(residue=residue) if not spinSystem: spinSystem = newSpinSystem(residue=residue, ccpCode=residue.ccpCode) spinSystem.secStrucCode = ssCode n += 1 showInfo('Info', 'Stored secondary structure types for %d residues.' % n, parent=self) def storeDihedralConstraints(self): if not self.dangleChain: return # make a new dihedralConstraintList head = self.constraintSet if not head: head = self.project.newNmrConstraintStore( nmrProject=self.nmrProject) self.constraintSet = head chain = self.dangleChain.chain shiftList = self.dangleChain.shiftList name = 'DANGLE Chain %s:%s ShiftList %d' % ( chain.molSystem.code, chain.code, shiftList.serial) constraintList = head.newDihedralConstraintList(name=name, measureListSerials=[ shiftList.serial, ]) # traverse the sequence and make appropriate constraint objects residues = chain.sortedResidues() for residue in residues: # Ensure we have atomSets etc getResidueMapping(residue) residueList = [(dr.residue.seqCode, dr.residue, dr) for dr in self.dangleChain.dangleResidues] residueList.sort() cnt = 0 for seqCode, residue, dangleResidue in residueList: phi = dangleResidue.phiValue psi = dangleResidue.psiValue if (phi is None) and (psi is None): continue # Use below functions because residues may not be sequentially numbered prevRes = getLinkedResidue(residue, 'prev') nextRes = getLinkedResidue(residue, 'next') if (prevRes is None) or (nextRes is None): continue C__1 = prevRes.findFirstAtom(name='C') # C (i-1) N_0 = residue.findFirstAtom(name='N') # N (i) CA_0 = residue.findFirstAtom(name='CA') # CA(i) C_0 = residue.findFirstAtom(name='C') # N (i) N_1 = nextRes.findFirstAtom(name='N') # C (i+1) # get fixedResonances fixedResonances = [] for atom in (C__1, N_0, CA_0, C_0, N_1): atomSet = atom.atomSet if atomSet.resonanceSets: resonance = atomSet.findFirstResonanceSet( ).findFirstResonance() else: # make new resonance if not atom.chemAtom: print 'no chem atom' ic = atom.chemAtom.elementSymbol if (ic == 'C'): ic = '13' + ic elif (ic == 'N'): ic = '15' + ic resonance = self.nmrProject.newResonance(isotopeCode=ic) assignAtomsToRes([ atomSet, ], resonance) fixedResonances.append(getFixedResonance(head, resonance)) # make dihedralConstraints phiResonances = (fixedResonances[0], fixedResonances[1], fixedResonances[2], fixedResonances[3]) phiConstraint = constraintList.newDihedralConstraint( resonances=phiResonances) psiResonances = (fixedResonances[1], fixedResonances[2], fixedResonances[3], fixedResonances[4]) psiConstraint = constraintList.newDihedralConstraint( resonances=psiResonances) # make constraint items if phi is not None: phiConstraint.newDihedralConstraintItem( targetValue=phi, upperLimit=dangleResidue.phiUpper, lowerLimit=dangleResidue.phiLower) cnt += 1 if psi is not None: psiConstraint.newDihedralConstraintItem( targetValue=psi, upperLimit=dangleResidue.psiUpper, lowerLimit=dangleResidue.psiLower) cnt += 1 showInfo('Success', 'DANGLE has generated %d dihedral restraints.' % cnt, parent=self) def loadGLEs(self, dRes, row, col): residue = dRes.residue title = '%d %s' % (residue.seqCode, residue.ccpCode) self.fillGlePlot(self.plot, dRes.phiPsiLikelihoodMatrix, title) prevDangleRes = self.getDangleResidue(dRes, 'prev') if prevDangleRes: self.fillGlePlot(self.prevPlot, prevDangleRes.phiPsiLikelihoodMatrix) else: self.fillGlePlot(self.prevPlot, [0] * 1296) # blank nextDangleRes = self.getDangleResidue(dRes, 'next') if nextDangleRes: self.fillGlePlot(self.nextPlot, nextDangleRes.phiPsiLikelihoodMatrix) else: self.fillGlePlot(self.nextPlot, [0] * 1296) # blank self.updatePhiPsi(dRes.residue) def fillGlePlot(self, plot, gleMatrix, title=None): scaleCol = plot.scaleColorQuick if self.colorScheme == 'black': plot.nullColor = '#000000' else: plot.nullColor = '#FFFFFF' itemconf = plot.canvas.itemconfigure matrix = plot.matrix for j in range(36): for i in range(36): v = gleMatrix[j * 36 + i] #if (v < 0.005): # color = plot.nullColor #else: color = self.getPlotColor(v) item = matrix[i][j] if plot.binWidth < 7: itemconf(item, fill=color, outline=color) elif plot.binWidth < 12: itemconf(item, fill=color, outline=scaleCol(color, 0.9)) else: itemconf(item, fill=color, outline=scaleCol(color, 0.8)) if title: itemconf(plot.title, text=title) def getDangleResidue(self, dRes, direction): # return a DangleResidue object located offset-residue away from dRes in sequence # Use below function to guard against non-sequentially numbered residues # the below function follows bonds, but uses a cache for speed residue = getLinkedResidue(dRes.residue, direction) if residue and self.dangleChain: return self.dangleChain.findFirstDangleResidue(residue=residue) def showPrevious(self): if not self.dangleResidue: return prevDangleResidue = self.getDangleResidue(self.dangleResidue, 'prev') if not prevDangleResidue: return self.predictionMatrix.selectObject(prevDangleResidue) #self.dangleResidue = prevDangleResidue #self.loadGLEs(self.dangleResidue, None, None) #self.predictionMatrix.currentObject = self.dangleResidue #self.predictionMatrix.hilightObject(self.predictionMatrix.currentObject) def showNext(self): if not self.dangleResidue: return nextDangleResidue = self.getDangleResidue(self.dangleResidue, 'next') if not nextDangleResidue: return self.predictionMatrix.selectObject(nextDangleResidue) #self.dangleResidue = nextDangleResidue #self.loadGLEs(self.dangleResidue, None, None) #self.predictionMatrix.currentObject = self.dangleResidue #self.predictionMatrix.hilightObject(self.predictionMatrix.currentObject) def updatePhiPsi(self, residue): if self.ensemble: phiPsiAccept = [] plotObjects = [] colors = [] cChain = self.ensemble.findFirstCoordChain(code=residue.chain.code) if cChain: cResidue = cChain.findFirstResidue(residue=residue) if cResidue: for model in self.ensemble.models: phiPsiAccept.append(self.getPhiPsi(cResidue, model)) plotObjects.append((cResidue, model)) colors.append(ENSEMBLE_COLOR) if self.colorScheme == 'rainbow': # default grey circles self.plot.updateObjects(phiPsiAccList=phiPsiAccept, objectList=plotObjects) else: # bright green circles self.plot.updateObjects(phiPsiAccList=phiPsiAccept, objectList=plotObjects, colors=colors) def getPhiPsi(self, residue, model=None): phi, psi = getResiduePhiPsi(residue, model=model) return (phi, psi, 1) def getPlotColor(self, i, maxInt=255): mode = self.colorScheme if mode == 'rainbow': if (i == 0): return '#%02x%02x%02x' % (255, 255, 255) # white bg elif (i > 0.75): red = 1 green = (1 - i) / 0.25 blue = 0 elif (i > 0.5): red = (i - 0.5) / 0.25 green = 1 blue = 0 elif (i > 0.25): red = 0 green = 1 blue = (0.5 - i) / 0.25 else: red = 0 green = i / 0.25 blue = 1 return '#%02x%02x%02x' % (red * maxInt, green * maxInt, blue * maxInt) """ elif mode == 'black': if i > 0.5: red = i green = 1 - i blue = 1 - i else: v = 0.1 + (0.9 * i) red = v green = v blue = v elif mode == 'white': if i > 0.5: red = i green = 1 - i blue = 1 - i else: v = 1.0 - (0.9 * i) red = v green = v blue = v return '#%02x%02x%02x' % (red*maxInt, green*maxInt, blue*maxInt) """ # default : red to black if (i == 0): return '#%02x%02x%02x' % (255, 255, 255) # white bg return '#%02x%02x%02x' % (((1 - i) * 255), 0, 0) def updatePredictionMatrixAfter(self, index=None, text=None): if self.chain and self.shiftList and self.dangleStore: self.dangleChain = self.dangleStore.findFirstDangleChain( chain=self.chain, shiftList=self.shiftList) else: self.dangleChain = None self.after_idle(self.updatePredictionMatrix) #if showYesNo('Not Found','No data for Chain %s in Dangle Run %s.\nMake prediction for this chain?' % (self.chain.code, text), parent=self): # self.runDangle() def updatePredictionMatrix(self): shiftList = self.shiftList objectList = [] textMatrix = [] colorMatrix = [] if self.dangleChain: residueList = [(dr.residue.seqCode, dr.residue, dr) for dr in self.dangleChain.dangleResidues] residueList.sort() else: # Chow blank table residueList = [] for seqCode, residue, dRes in residueList: objectList.append(dRes) phi = dRes.phiValue psi = dRes.psiValue ss = dRes.secStrucCode atomNames = [] for atomName in BACKBONE_ATOMS: atom = residue.findFirstAtom(name=atomName) if not atom: continue atomSet = atom.atomSet if atomSet: shifts = getAtomSetShifts(atomSet, shiftList=shiftList) if shifts: atomNames.append(atomName) atomNames.sort() atomNames = ' '.join(atomNames) textMatrix.append((seqCode, residue.ccpCode, dRes.numIslands, ss, phi, psi, dRes.phiUpper, dRes.phiLower, dRes.psiUpper, dRes.psiLower, atomNames)) if (phi is None) and (psi is None): colorMatrix.append(INACTIVE_COLORS) elif dRes.numIslands >= 5: colorMatrix.append(BAD_COLORS) else: colorMatrix.append(GOOD_COLORS) self.predictionMatrix.update(textMatrix=textMatrix, objectList=objectList, colorMatrix=colorMatrix) def selectCell(self, dRes, row, col): self.dangleResidue = dRes self.row = row self.col = col self.loadGLEs(dRes, row, col) def setFloatEntry(self, event): index = self.col - 4 # index of attribute to set in the EDIT_ATTRS list value = self.floatEntry.get() if value is not None: setattr(self.dangleResidue, EDIT_ATTRS[index], value) self.updatePredictionMatrixAfter() def getFloatEntry(self, dangleResidue): if dangleResidue: index = self.col - 4 # index of attribute to set in the EDIT_ATTRS list self.floatEntry.set(getattr(dangleResidue, EDIT_ATTRS[index])) def changeChain(self, chain): if chain is not self.chain: self.chain = chain self.updateEnsemblePulldown( ) # Ensembles are filtered by chains molSystem self.updatePredictionMatrixAfter() def changeShiftList(self, shiftList): if shiftList is not self.shiftList: self.shiftList = shiftList self.updatePredictionMatrixAfter() def changeEnsemble(self, ensemble): self.ensemble = ensemble def changeConstraintSet(self, constraintSet): if constraintSet is not self.constraintSet: self.constraintSet = constraintSet def changeDangleStore(self, dangleStore): if self.dangleStore is not dangleStore: self.dangleStore = dangleStore if dangleStore: self.dangleChain = dangleStore.findFirstDangleChain() self.chain = self.dangleChain.chain self.shiftList = self.dangleChain.shiftList else: self.dangleChain = None self.updateChainPulldown() self.updateShiftListPulldown() self.updateEnsemblePulldown( ) # Ensembles are filtered by chains molSystem self.updatePredictionMatrixAfter() def checkDangleStore(self): if not self.dangleStore: N = len(self.project.dangleStores) + 1 name = askString('Request', 'Dangle Run Name:', 'Run%d' % N, parent=self) if not name: return None for character in whitespace: if character in name: showWarning('Failure', 'Name cannot contain whitespace', parent=self) return None if self.project.findFirstDangleStore(name=name): showWarning('Failure', 'Name already used', parent=self) return None self.dangleStore = self.project.newDangleStore(name=name) self.updateDangleStorePulldown() def updateChainPulldown(self, obj=None): index = 0 names = [] chains = [] chain = self.chain for molSystem in self.project.molSystems: msCode = molSystem.code for chainA in molSystem.chains: residues = chainA.residues if not residues: continue for residue in residues: # Must have at least one protein residue if residue.molType == 'protein': names.append('%s:%s' % (msCode, chainA.code)) chains.append(chainA) break if chains: if chain not in chains: chain = chains[0] index = chains.index(chain) else: chain = None if chain is not self.chain: self.chain = chain self.updatePredictionMatrixAfter() self.chainPulldown.setup(names, chains, index) def updateShiftListPulldown(self, obj=None): index = 0 names = [] shiftLists = getShiftLists(self.nmrProject) if shiftLists: if self.shiftList not in shiftLists: self.shiftList = shiftLists[0] index = shiftLists.index(self.shiftList) names = ['%s:%d' % (sl.name, sl.serial) for sl in shiftLists] else: self.shiftList = None self.shiftListPulldown.setup(names, shiftLists, index) def updateDangleStorePulldown(self): names = [ '<New>', ] dangleStores = [ None, ] for dangleStore in self.project.sortedDangleStores(): names.append(dangleStore.name) dangleStores.append(dangleStore) if self.dangleStore not in dangleStores: self.dangleStore = dangleStores[-1] index = dangleStores.index(self.dangleStore) self.dangleStorePulldown.setup(names, dangleStores, index) def updateEnsemblePulldown(self, obj=None): index = 0 names = [ '<None>', ] ensembles = [ None, ] if self.chain: molSystem = self.chain.molSystem for ensemble in molSystem.sortedStructureEnsembles(): names.append('%s:%d' % (molSystem.code, ensemble.ensembleId)) ensembles.append(ensemble) if self.ensemble not in ensembles: self.ensemble = ensembles[0] index = ensembles.index(self.ensemble) self.ensemblePulldown.setup(names, ensembles, index) def updateConstrSetPulldown(self, obj=None): names = [ '<New>', ] constraintSets = [ None, ] # Use below later, once API speed/loading is improved # for constraintSet in self.nmrProject.sortedNmrConstraintStores(): for constraintSet in self.project.sortedNmrConstraintStores(): names.append('%d' % constraintSet.serial) constraintSets.append(constraintSet) if self.constraintSet not in constraintSets: self.constraintSet = constraintSets[0] index = constraintSets.index(self.constraintSet) self.constrSetPulldown.setup(names, constraintSets, index) def try1(self): if not self.project: return ccpCodes = [ 'Ala', 'Cys', 'Asp', 'Glu', 'Phe', 'Gly', 'His', 'Ile', 'Lys', 'Leu', 'Met', 'Asn', 'Gln', 'Arg', 'Ser', 'Thr', 'Val', 'Trp', 'Tyr', 'Pro' ] atomNames = ['HA', 'CA', 'CB', 'C', 'N'] molType = 'protein' for ccpCode in ccpCodes: for atomName in atomNames: chemAtomNmrRef = getChemAtomNmrRef(self.project, atomName, ccpCode, molType) mean = chemAtomNmrRef.meanValue sd = chemAtomNmrRef.stdDev print '%5s%5s %.3f %.3f' % (ccpCode, atomName, mean, sd)
class InitRootAssignmentsPopup(BasePopup): """ **Add Starting Resonances and Spin Systems to Root Spectra** The purpose of this popup window is to setup certain kinds of spectra at the very start of the assignment process. This initialisation involves adding new, anonymous resonances and spin system (residue) assignments to picked, but unassigned peaks. The kinds of spectra that have their peak lists processed in this way are normally those that, on the whole, have one peak for each residue in the molecular system. This typically involves 15N HSQC and HNCO spectra where you get one peak for each NH group. In these instances two resonance are added for each peak; one for the 15N dimension and one for the 1H dimension, both resonances for the peak are then added to the same spin system (a resonance grouping that stands-in for a residue). The initial resonances and spin system groups that have been added to an initialised "root" spectrum then serve as the basis for assigning related spectra, often with higher dimensionality. The general principle is that the positions of the peaks, and hence the chemical shifts of their assigned resonances, serve as guides to pick, locate and assign peaks in other spectra that share the same resonances in some of their dimensions. For example peaks in a 15N HSQC spectrum can be used to link equivalent peaks in a 3D HNCA spectrum or 3D 15N HSQC-NOSEY spectrum of the same sample because peaks in the 3D spectra derive from the same amide groups. Once related spectra have been linked via assignments to a common set of resonances and sequential assignment has been performed the resonances and spin systems will no longer be anonymous; the resonances will be assigned to specific atoms, and the spin systems will be assigned to residues. One complication of the initialisation process is that not all peaks in the root spectra relate to unique residues or spin systems. The most common such examples are peaks that derive from NH2 (amide) side chain groups of Glutamine and Asparagine residues. In an 15N HSQC spectrum an NH2 group will give rise to two peaks one for each hydrogen, but these peaks share the same nitrogen and thus appear at the same 15N frequency. When a root spectrum is initialised such peaks must be taken care of in the appropriate way; the two peaks of an NH2 group share the same nitrogen resonance *and* the same spin system number. The "Amide Side Chain Peaks" table in the popup window lists all of the pairs of peaks that have similar 15N resonance positions, and which lie in the chemical shift regions for NH2 groups. The purpose of the table is for the user to confirm or deny that the pairs of peak really are NH2 groups, and not coincidental matches. The user can locate a potential NH2 peak pair by selecting an appropriate spectrum window and, assuming "Follow Amides" is set, clicking on the row for the peak pair. If the peaks look like a genuine NH2 pair (similar intensity, deuterium sub-peak etc) then the peaks are may be confirmed by double clicking in the "Confirmed?" column. With any NH2 groups confirmed, the peak list is initialised via [Initialise Peak List!] at the top; all peaks will have new resonances and spin systems added, and peaks from NH2 groups will be linked appropriately. **Caveats & Tips** This initialisation tool is currently limited to the following types of experiment: 15N HSQC, HNCO, HNcoCA, 13C HSQC. Only potential NH2 peak pairs with 15N positions that are within the stated tolerance are shown. This tolerance can be reduced to make the amide search more specific, although this may be at the expense of detecting NH2 pairs that are distorted due to peak overlap. """ def __init__(self, parent, *args, **kw): self.waiting = False self.peakList = None self.amidePair = None self.amidePairs = [] self.isAmide = {} self.guiParent = parent self.windowPane = None self.marks = [] BasePopup.__init__(self, parent=parent, title='Assignment : Initialise Root Resonances', **kw) def open(self): self.update() BasePopup.open(self) def close(self): self.clearMarks() BasePopup.close(self) def body(self, guiFrame): self.geometry('550x600') guiFrame.grid_columnconfigure(0, weight=1) row = 0 frame = Frame(guiFrame, grid=(row, 0)) frame.expandGrid(0, 4) label = Label(frame, text='Peak List:', grid=(0, 0)) tipText = 'For spectra with (mostly) one peak per residue, selects which peak list to initialise; by adding anonymous resonance and spin system numbers' self.peakListPulldown = PulldownList(frame, self.changePeakList, grid=(0, 1), tipText=tipText) label = Label(frame, text='15N tolerance (ppm):', grid=(0, 2)) tipText = 'The upper limit in the difference between 15N ppm positions for two peaks to be considered as a potential amide' self.tolEntry = FloatEntry(frame, text=0.02, width=8, grid=(0, 3), sticky='ew', tipText=tipText) frame2 = Frame(frame, grid=(1, 0), gridSpan=(1, 4), sticky='ew') frame2.grid_columnconfigure(5, weight=1) label = Label(frame2, text='Follow Amides:', grid=(0, 0)) tipText = 'Sets whether to follow the H-N locations of potential amide peaks when clicking in rows of the table' self.followSelect = CheckButton(frame2, callback=None, grid=(0, 1), selected=True, tipText=tipText) self.windowLabel = Label(frame2, text='Window:', grid=(0, 2)) tipText = 'Selects the spectrum window in which to show positions of potential amide peaks' self.windowPulldown = PulldownList(frame2, self.selectWindowPane, grid=(0, 3), tipText=tipText) label = Label(frame2, text='Mark Amides:', grid=(0, 4)) tipText = 'Whether to put a multi-dimensional cross mark though the H-N positions of the selected peak pair' self.markSelect = CheckButton(frame2, callback=None, grid=(0, 5), selected=True, tipText=tipText) utilButtons = UtilityButtonList(guiFrame, grid=(row, 1), sticky='ne', helpUrl=self.help_url) row += 1 tipTexts = [ 'For the stated peak list, considering confirmed amide side chain peaks, add spin system and resonance numbers to all peaks', ] commands = [ self.initialisePeakList, ] texts = [ 'Initialise Peak List!', ] self.initButtons = ButtonList(guiFrame, commands=commands, grid=(row, 0), texts=texts, gridSpan=(1, 2), tipTexts=tipTexts) self.initButtons.buttons[0].config(bg='#B0FFB0') row += 1 div = LabelDivider(guiFrame, text='Amide Side Chain Peaks', gridSpan=(1, 2), grid=(row, 0)) row += 1 guiFrame.grid_rowconfigure(row, weight=1) frame = Frame(guiFrame, gridSpan=(1, 2), grid=(row, 0)) frame.expandGrid(0, 0) editSetCallbacks = [None] * 8 editGetCallbacks = [None] * 8 editWidgets = [None] * 8 editGetCallbacks[1] = self.toggleConfirm #self.userCodeEntry = Entry(self,text='', returnCallback=self.setResidueCode, width=6) tipTexts = [ 'Number of the peak pair for the table', 'Whether the peak pair is confirmed as being from an amide side chain; a common nitrogen by different hydrogens', 'The difference in 15N ppm position between the two peaks of the pair', 'The assignment annotation of the first peak in the pair', 'The assignment annotation of the second peak in the pair', 'The average 15N position of the peaks in the pair', 'The 1H chemical shift of the first peak in the pair', 'The 1H chemical shift of the second peak in the pair' ] headingList = [ '#', 'Confirmed?', u'15N\n\u0394ppm', 'Peak 1', 'Peak 2', 'Shift N', 'Shift H1', 'Shift H2' ] self.amidePairMatrix = ScrolledMatrix( frame, headingList=headingList, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, callback=self.selectAmidePair, multiSelect=True, grid=(0, 0), tipTexts=tipTexts) tipTexts = [ 'Confirm the selected peak pairs as being amide side chain peaks; a common nitrogen by different hydrogens', 'remove any conformation for the selected peak pairs being amide side chain peaks', 'Manually force an update the table of potential pairs of amide side chain peaks' ] commands = [ self.confirmAmidePairs, self.unconfirmAmidePairs, self.predictAmidePairs ] texts = ['Confirm\nSelected', 'Unconfirm\nSelected', 'Update\nTable'] self.amideButtons = ButtonList(frame, texts=texts, commands=commands, grid=(1, 0), tipTexts=tipTexts) self.updatePeakLists() self.administerNotifiers(self.registerNotify) def toggleConfirm(self, peakPair): peakA, peakB = peakPair boolean = not peakA.amideConfirmed peakA.amideConfirmed = boolean peakB.amideConfirmed = boolean self.updateAfter() def confirmAmidePairs(self): for peakA, peakB in self.amidePairMatrix.currentObjects: peakA.amideConfirmed = True peakB.amideConfirmed = True self.updateAfter() def unconfirmAmidePairs(self): for peakA, peakB in self.amidePairMatrix.currentObjects: peakA.amideConfirmed = False peakB.amideConfirmed = False self.updateAfter() def updatePeakListsAfter(self, obj): self.after_idle(self.updatePeakLists) def updateWindowListsAfter(self, obj): self.after_idle(self.updateWindowList) def updateWindowList(self): index = 0 names = [] windowPane = self.windowPane windowPanes = self.getWindows() if windowPanes: names = [getWindowPaneName(wp) for wp in windowPanes] if windowPane not in windowPanes: windowPane = windowPanes[0] index = windowPanes.index(windowPane) else: windowPane = None if windowPane is not self.windowPane: self.selectWindowPane(windowPane) self.windowPulldown.setup(names, windowPanes, index) def selectWindowPane(self, windowPane): if windowPane is not self.windowPane: self.windowPane = windowPane def peakUpdateAfter(self, peak): if self.waiting: return if peak.peakList is self.peakList: self.updateAfter() def peakDimUpdateAfter(self, peakDim): if self.waiting: return if peakDim.peak.peakList is self.peakList: self.updateAfter() def contribUpdateAfter(self, contrib): if self.waiting: return if contrib.peakDim.peak.peakList is self.peakList: self.updateAfter() def markPeaks(self): if self.amidePair: peakA, peakB = self.amidePair markA = createPeakMark(peakA, lineWidth=2.0, remove=False) markB = createPeakMark(peakB, lineWidth=2.0, remove=False) self.clearMarks() self.marks = [markA, markB] def clearMarks(self): for mark in self.marks: if not mark.isDeleted: mark.delete() self.marks = [] def followPeaks(self): if self.amidePair and self.windowPane: self.windowPane.getWindowFrame() zoomToShowPeaks(self.amidePair, self.windowPane) def getWindows(self): windows = [] if self.peakList: project = self.peakList.root spectrum = self.peakList.dataSource tryWindows = getActiveWindows(project) for window in tryWindows: for windowPane in window.sortedSpectrumWindowPanes(): if isSpectrumInWindowPane(windowPane, spectrum): windows.append(windowPane) return windows def getPeakLists(self): peakLists = [] for experiment in self.nmrProject.experiments: refExperiment = experiment.refExperiment if refExperiment and (refExperiment.name in ALLOWED_REF_EXPTS): for spectrum in experiment.dataSources: if spectrum.dataType == 'processed': for peakList in spectrum.peakLists: peakLists.append( (self.getPeakListName(peakList), peakList)) peakLists.sort() return [x[1] for x in peakLists] def getPeakListName(self, peakList): spectrum = peakList.dataSource return '%s:%s:%d' % (spectrum.experiment.name, spectrum.name, peakList.serial) def updatePeakLists(self): index = 0 names = [] peakList = self.peakList peakLists = self.getPeakLists() if peakLists: names = [self.getPeakListName(pl) for pl in peakLists] if peakList not in peakLists: peakList = peakLists[0] index = peakLists.index(peakList) else: peakList = None if peakList is not self.peakList: self.changePeakList(peakList) self.peakListPulldown.setup(names, peakLists, index) def changePeakList(self, peakList): if peakList is not self.peakList: self.peakList = peakList self.predictAmidePairs() self.updateAfter() self.updateButtons() self.updateWindowList() def selectAmidePair(self, obj, row, col): self.amidePair = obj if self.followSelect.get(): self.followPeaks() if self.markSelect.get(): self.markPeaks() self.updateButtons() def updateAfter(self): if not self.waiting: self.waiting = True self.after_idle(self.update) def update(self): textMatrix = [] objectList = [] colorMatrix = [] #['#','Delta\n15N ppm','Peak 1','Peak 2','Shift N','Shift H1','Shift H2'] i = 0 for amidePair in self.amidePairs: peakA, peakB, delta, ppmN, ppmH1, ppmH2 = amidePair i += 1 colors = [None] * 8 if peakA.amideConfirmed and peakB.amideConfirmed: colors[1] = '#B0FFB0' confText = 'Yes' else: colors[1] = '#FFB0B0' confText = 'No' datum = [ i, confText, delta, '\n'.join([ pd.annotation or '-' for pd in peakA.sortedPeakDims() ]), '\n'.join( [pd.annotation or '-' for pd in peakB.sortedPeakDims()]), ppmN, ppmH1, ppmH2 ] textMatrix.append(datum) objectList.append((peakA, peakB)) colorMatrix.append(colors) self.amidePairMatrix.update(textMatrix=textMatrix, objectList=objectList, colorMatrix=colorMatrix) self.waiting = False def updateButtons(self): buttons = self.amideButtons.buttons buttons2 = self.initButtons.buttons if self.peakList: buttons2[0].enable() buttons[2].enable() if self.amidePair: buttons[0].enable() buttons[1].enable() else: buttons[0].disable() buttons[1].disable() else: buttons[0].disable() buttons[1].disable() buttons[2].disable() def predictAmidePairs(self): self.amidePair = None isAmide = self.isAmide = {} amidePairs = self.amidePairs = [] peakList = self.peakList if peakList: tol = self.tolEntry.get() or 0.001 spectrum = self.peakList.dataSource dimPairs = getOnebondDataDims(spectrum) if not dimPairs: return isotopes = getSpectrumIsotopes(spectrum) hDim = None for dimA, dimB in dimPairs: i = dimA.dim - 1 j = dimB.dim - 1 if ('1H' in isotopes[i]) and ('1H' not in isotopes[j]): hDim = i nDim = j elif ('1H' in isotopes[j]) and ('1H' not in isotopes[i]): hDim = j nDim = i if hDim is not None: break isAssigned = {} matchPeaks = [] for peak in peakList.peaks: if isAmide.get(peak): continue peakDims = peak.sortedPeakDims() ppmN = peakDims[nDim].realValue ppmH = peakDims[hDim].realValue for contrib in peakDims[nDim].peakDimContribs: resonance = contrib.resonance for contrib2 in resonance.peakDimContribs: peak2 = contrib2.peakDim.peak if (peak2.peakList is peakList) and (peak2 is not peak): isAmide[peak] = peak2 isAmide[peak2] = peak if hasattr(peak, 'amideConfirmed'): peak2.amideConfirmed = peak.amideConfirmed else: peak.amideConfirmed = True peak2.amideConfirmed = True ppmH2 = peak2.findFirstPeakDim(dim=hDim + 1).value ppmN2 = peak2.findFirstPeakDim(dim=nDim + 1).value ppmNm = 0.5 * (ppmN + ppmN2) deltaN = abs(ppmN - ppmN2) amidePairs.append( (peak, peak2, deltaN, ppmNm, ppmH, ppmH2)) break else: continue break else: if ppmN > 122.0: continue elif ppmN < 103: continue if ppmH > 9.0: continue elif ppmH < 5.4: continue if not hasattr(peak, 'amideConfirmed'): peak.amideConfirmed = False matchPeaks.append((ppmN, ppmH, peak)) N = len(matchPeaks) unassignedPairs = [] for i in range(N - 1): ppmN, ppmH, peak = matchPeaks[i] for j in range(i + 1, N): ppmN2, ppmH2, peak2 = matchPeaks[j] deltaN = abs(ppmN2 - ppmN) if deltaN > tol: continue if abs(ppmH - ppmH2) > 1.50: continue ppmNm = 0.5 * (ppmN + ppmN2) isAmide[peak] = peak2 isAmide[peak2] = peak unassignedPairs.append( (deltaN, peak, peak2, ppmNm, ppmH, ppmH2)) unassignedPairs.sort() done = {} for deltaN, peak, peak2, ppmNm, ppmH, ppmH2 in unassignedPairs: if done.get(peak): continue if done.get(peak2): continue done[peak] = True done[peak2] = True amidePairs.append((peak, peak2, deltaN, ppmNm, ppmH, ppmH2)) peaks = isAmide.keys() for peak in peaks: if done.get(peak) is None: del isAmide[peak] self.updateAfter() def initialisePeakList(self): isAmide = self.isAmide peakList = self.peakList if not peakList: showWarning('Warning', 'No peak list available or selected', parent=self) return experimentType = peakList.dataSource.experiment.refExperiment.name for peak in peakList.peaks: peakDims = peak.sortedPeakDims() spinSystem = None resonances = [] for peakDim in peakDims: dataDimRef = peakDim.dataDimRef if not dataDimRef: continue isotopes = dataDimRef.expDimRef.isotopeCodes if not peakDim.peakDimContribs: if '15N' in isotopes: if hasattr( peak, 'amideConfirmed' ) and peak.amideConfirmed and isAmide.get(peak): peakDim2 = isAmide[peak].findFirstPeakDim( dim=peakDim.dim) contrib2 = peakDim2.findFirstPeakDimContrib() if contrib2: contrib = assignResToDim( peakDim, contrib2.resonance) else: contrib = assignResToDim(peakDim) assignResToDim(peakDim2, contrib.resonance) else: contrib = assignResToDim(peakDim) else: contrib = assignResToDim(peakDim) else: contrib = peakDim.findFirstPeakDimContrib() if not contrib: continue resonance = contrib.resonance if '13C' in isotopes: if experimentType == 'H[N[CO]]': resonance.setAssignNames([ 'C', ]) elif experimentType == 'H[N[co[CA]]]': resonance.setAssignNames([ 'CA', ]) continue resonances.append(resonance) if isAmide.get(peak) and hasattr( peak, 'amideConfirmed') and peak.amideConfirmed: continue if ('1H' in isotopes) and (experimentType != 'H[C]'): resonance.setAssignNames([ 'H', ]) elif '15N' in isotopes: resonance.setAssignNames([ 'N', ]) for resonance in resonances: if resonance.resonanceGroup: spinSystem = resonance.resonanceGroup for resonance in resonances: if spinSystem is None: spinSystem = findSpinSystem(resonance) addSpinSystemResonance(spinSystem, resonance) def administerNotifiers(self, notifyFunc): for func in ('__init__', 'delete'): for clazz in ('ccp.nmr.Nmr.PeakDimContrib', ): notifyFunc(self.contribUpdateAfter, clazz, func) for func in ('__init__', 'delete', 'setName'): for clazz in ('ccp.nmr.Nmr.PeakList', 'ccp.nmr.Nmr.DataSource', 'ccp.nmr.Nmr.Experiment'): notifyFunc(self.updatePeakListsAfter, clazz, func) notifyFunc(self.updatePeakListsAfter, 'ccp.nmr.Nmr.Experiment', 'setRefExperiment') for func in ('__init__', 'delete', 'setName'): notifyFunc(self.updateWindowListsAfter, 'ccpnmr.Analysis.SpectrumWindow', func) for func in ('__init__', 'delete', 'setName'): notifyFunc(self.updateWindowListsAfter, 'ccpnmr.Analysis.SpectrumWindowPane', func) for func in ('__init__', 'delete', 'setAnnotation'): notifyFunc(self.peakUpdateAfter, 'ccp.nmr.Nmr.Peak', func) for func in ('setAnnotation', 'setPosition', 'setNumAliasing'): notifyFunc(self.peakDimUpdateAfter, 'ccp.nmr.Nmr.PeakDim', func) def destroy(self): self.administerNotifiers(self.unregisterNotify) self.clearMarks() BasePopup.destroy(self)
class SpinSystemTypingPopup(BasePopup): """ **Predict Which Types of Residue Spin Systems Represent** This popup window uses chemical shift information, obtained from the resonances of a spin system group, to predict which kind of residue a spin system could be. Naturally, the more resonances/shifts there are in a spin system the better the prediction will be. Predictions are either made for a single spin system in isolation, or for a whole chain; by shuffling the residue to spin system mapping to give the optimum arrangement. Prediction for individual spin systems, as accessed by [Show Individual Classification], is covered in the `Spin System Type Scores`_ popup and will not be discussed here. .. _`Spin System Type Scores`: ../popups/SpinSystemTypeScoresPopup.html **Finding the Optimal Arrangement of Residue Types** This system attempts to find the best match of spin system to residue type by performing a Monte Carlo search to arrange each group of chemical shifts amongst the residue types found in the chain. The main principle is that the chain's residues dictate how many spin systems of a given type may be found, such that for example if there is only one Threonine residue then only one spin system may be predicted to be of Threonine type. This result comes naturally from shuffling the spin systems, with their chemical shifts, amongst the residue slots (disregarding sequence position). The prediction is made my selecting the chain and shift list to use, then which kinds of isotope to consider, by toggling the relevant buttons, choosing various Monte Carlo search options and finally selecting [Run Typing]. The prediction may take some time to run, depending upon the number of residues and spin systems that are being matched, but gives a graphical output of the progress. If the final prediction looks good the "Highest Scoring Mappings" display may be closed and [Assign Types] may be used to set the residue types for all of the spin systems in the main table that match only a single type and have a score above the assignment threshold value. Spin systems that already have a type or full residue assignment will not be affected. The default Monte Carlo search options ought to be appropriate for a small protein (100 residues) with 1H and 13C chemical shift information. Increasing the number of search steps may help if the search does not converge; still swaps between optimal assignments toward the end of the search. Increasing the ensemble size (how many test mappings are optimised at the same time) may help if the prediction gets stuck in local minima; different runs predict different arrangement, but larger ensembles require more search steps to converge. Where a spin system has multiple residue types predicted these are the types that come out of the final ensemble for that set of shifts, i.e. at this point the ensemble of solutions differ. Overall, it should be noted that if a human being cannot readily predict the probably types of a spin system from its shifts alone, then this search tool cannot be expected to do a good job; it is merely an optimiser to address the problem of shuffling spin systems within a chain. The scores are currently unnormalised log-odds values and are not especially meaningful in the human sense, other than higher is better (closer to zero for negative values). This issue will be addressed in the future. Spin systems without a unique type prediction will not get a final score. """ def __init__(self, parent, *args, **kw): self.guiParent = parent self.chain = None self.waiting = False self.shiftList = None self.spinSystem = None self.preserveTypes = 0 self.progressBar = None self.scoresPopup = None self.threshold = -20.0 BasePopup.__init__(self, parent, title="Assignment : Spin System Typing", **kw) def body(self, guiFrame): guiFrame.grid_columnconfigure(3, weight=1) self.progressBar = TypingEnsemblePopup(self,total=100) self.progressBar.close() row = 0 label = Label(guiFrame, text=' Chain: ', grid=(row,0)) tipText = 'Selects which molecular chain the spin system residue types will be predicted for; determines which range of types are available' self.chainPulldown = PulldownList(guiFrame, self.changeChain, grid=(row,1), tipText=tipText) tipText = 'Selects which shift list will be used as the source of chemical shift information to make the residue type predictions' label = Label(guiFrame, text='Shift List: ', grid=(row,2)) self.shiftListPulldown = PulldownList(guiFrame, callback=self.setShiftList, grid=(row,3), tipText=tipText) utilButtons = UtilityButtonList(guiFrame, helpUrl=self.help_url) utilButtons.grid(row=row, column=4, sticky='w') row += 1 frame = LabelFrame(guiFrame, text='Options', grid=(row,0), gridSpan=(1,5)) frame.grid_columnconfigure(3, weight=1) frow = 0 label = Label(frame, text='Keep existing types?', grid=(frow,0), sticky='e') tipText = 'Whether any existing residue type information should be preserved, when predicting the type of others' self.preserveTypesSelect = CheckButton(frame, grid=(frow,1), selected=False, callback=self.selectPreserveTypes, tipText=tipText) label = Label(frame, text='Assignment threshold: ', grid=(frow,2), sticky='e') tipText = 'The lower limit for the predicted residue type to be set with "Assign Types"; needs to be adjusted according to result statistics and amount of shift data' self.thresholdEntry = FloatEntry(frame, text=self.threshold, width=8, grid=(frow,3), tipText=tipText) frow += 1 label = Label(frame, text='Ensemble size: ', grid=(frow,0), sticky='e') tipText = 'The number of best scoring residue type mappings, from the Monte Carlo search, to use un the prediction' self.ensembleEntry = IntEntry(frame,text=20,width=4, grid=(frow,1), tipText=tipText) label = Label(frame, text='Num Search Steps: ', grid=(frow,2), sticky='e') tipText = 'The number of iterative steps that will be used in the Monte Carlo search of best spin system to residue type mappings' self.stepsEntry = IntEntry(frame, text=100000, width=8, tipText=tipText, grid=(frow,3)) frow += 1 label = Label(frame, text='Isotope shifts to consider:', grid=(frow,0), gridSpan=(1,4)) frow += 1 self.isotopes = ['1H','13C'] isos = ['1H','13C','15N'] colors = [COLOR_DICT[x] for x in isos] tipText = 'Selects which kinds of resonances, in terms of isotope, the residue type predictions will be made with' self.isotopeCheckButtons = PartitionedSelector(frame, labels=isos, objects=isos, colors=colors, callback=self.toggleIsotope, selected=self.isotopes, grid=(frow,0), gridSpan=(1,4), tipText=tipText) row += 1 guiFrame.grid_rowconfigure(row, weight=1) labelFrame = LabelFrame(guiFrame, text='Spin Systems', grid=(row,0), gridSpan=(1,5)) labelFrame.expandGrid(0,0) tipTexts = ['The spin system serial number', 'The residue to which the spin system may currently be assigned', 'Set whether to include a particular spin system in the type predictions', 'The spin system to residue type match score for a prediction; higher (less negative) is better', 'The predicted types of residue that the spin system may be', 'The chemical shifts in the spin system that will be used in the analysis'] headingList = ['#','Residue','Use?','Score','Types','Shifts'] justifyList = ['center','center','center','center','center','left'] editWidgets = [None, None, None, None, None, None] editGetCallbacks = [None, None, self.toggleInclude, None, None, None] editSetCallbacks = [None, None, None, None, None, None] self.scrolledMatrix = ScrolledMatrix(labelFrame, headingList=headingList, justifyList=justifyList, editSetCallbacks=editSetCallbacks, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, callback=self.selectCell, grid=(0,0), tipTexts=tipTexts) row += 1 tipTexts = ['Execute the Monte Carlo search that will make the residue type predictions for the spin systems', 'Assign the residue type of spin systems with a unique type prediction and prediction score above the stated threshold', 'Show a residue type prediction for the selected spin system alone; only considers that spin system of shifts, not how all spin systems fit to the sequence', 'Show a table of peaks that are assigned to the resonances of the selected spin system'] texts = ['Run\nTyping','Assign\nTypes', 'Show Individual\nClassification', 'Show\nPeaks'] commands = [self.run, self.assign, self.individualScore, self.showPeaks] bottomButtons = ButtonList(guiFrame, texts=texts, commands=commands, grid=(row,0), gridSpan=(1,5), tipTexts=tipTexts) self.runButton = bottomButtons.buttons[0] self.assignButton = bottomButtons.buttons[1] self.scoreButton = bottomButtons.buttons[2] self.peaksButton = bottomButtons.buttons[2] self.runButton.config(bg='#B0FFB0') for func in ('__init__','delete'): self.registerNotify(self.updateChains, 'ccp.molecule.MolSystem.Chain', func) self.registerNotify(self.updateShiftLists, 'ccp.nmr.Nmr.ShiftList', func) for func in ('__init__','delete','setCcpCode', 'setResidue','addResonance', 'setName', 'removeResonance','setResonances'): self.registerNotify(self.updateSpinSystemsAfter, 'ccp.nmr.Nmr.ResonanceGroup', func) self.updateChains() self.updateShiftLists() self.updateSpinSystems() def individualScore(self): if self.scoresPopup: self.scoresPopup.open() self.scoresPopup.update(self.spinSystem, self.chain) else: self.scoresPopup = SpinSystemTypeScoresPopup(self.guiParent, spinSystem=self.spinSystem, chain=self.chain) def showPeaks(self): if self.spinSystem: peaks = [] for resonance in self.spinSystem.resonances: for contrib in resonance.peakDimContribs: peaks.append(contrib.peakDim.peak) if len(peaks) > 0: self.guiParent.viewPeaks(peaks) def assign(self): for spinSystem in self.scrolledMatrix.objectList: if spinSystem.sstSelected == 'Yes': if (len(spinSystem.sstTypes) == 1) and (spinSystem.ssScore > self.threshold): ccpCode = spinSystem.sstTypes[0] if spinSystem.residue and (spinSystem.residue.ccpCode != ccpCode): assignSpinSystemResidue(spinSystem,residue=None) if spinSystem.ccpCode != ccpCode: assignSpinSystemType(spinSystem,ccpCode,'protein') self.updateSpinSystems() def toggleIsotope(self, isotope): if isotope in self.isotopes: self.isotopes.remove(isotope) else: self.isotopes.append(isotope) self.updateSpinSystemsAfter() def selectPreserveTypes(self, boolean): self.preserveTypes = boolean def toggleInclude(self, *obj): if self.spinSystem: if self.spinSystem.sstSelected == 'Yes': self.spinSystem.sstSelected = 'No' else: self.spinSystem.sstSelected = 'Yes' self.updateSpinSystemsAfter() def selectCell(self, spinSystem, row, col): self.spinSystem = spinSystem self.updateButtons() def updateShiftLists(self, *opt): shiftLists = getShiftLists(self.nmrProject) names = ['Shift List %d' % x.serial for x in shiftLists] index = 0 if shiftLists: if self.shiftList not in shiftLists: self.shiftList = shiftLists[0] index = shiftLists.index(self.shiftList) self.shiftListPulldown.setup(names, shiftLists, index) def setShiftList(self, shiftList): self.shiftList = shiftList self.updateSpinSystemsAfter() def updateButtons(self): if self.chain and self.scrolledMatrix.objectList: self.runButton.enable() self.assignButton.enable() else: self.runButton.enable() self.assignButton.enable() if self.spinSystem: self.scoreButton.enable() self.peaksButton.enable() else: self.scoreButton.disable() self.peaksButton.disable() def updateSpinSystemsAfter(self, spinSystem=None): if spinSystem: spinSystem.sstTypes = [] spinSystem.ssScore = None spinSystem.codeScoreDict = {} if self.waiting: return else: self.waiting = True self.after_idle(self.updateSpinSystems) def updateSpinSystems(self): textMatrix = [] objectList = [] if self.project: for spinSystem in self.nmrProject.resonanceGroups: if not spinSystem.resonances: continue if self.chain: if spinSystem.residue and (spinSystem.residue.chain is not self.chain): continue if spinSystem.chains and (self.chain not in spinSystem.chains): continue if hasattr(spinSystem, 'sstSelected'): includeText = spinSystem.sstSelected else: spinSystem.sstSelected = 'Yes' includeText = 'Yes' if not hasattr(spinSystem, 'sstTypes'): spinSystem.sstTypes = [] if not hasattr(spinSystem, 'ssScore'): spinSystem.ssScore = None if spinSystem.ssScore: scoreText = '%.2f' % spinSystem.ssScore else: scoreText = None typesText = ' '.join(spinSystem.sstTypes) residueText = '' if spinSystem.residue: resCode = getResidueCode(spinSystem.residue) residueText = '%d%s' % (spinSystem.residue.seqCode,resCode) elif spinSystem.residueProbs: resTexts = [] resSeqs = [] resCodes = set() for residueProb in spinSystem.residueProbs: if not residueProb.weight: continue residue = residueProb.possibility seq = residue.seqCode resCode = getResidueCode(residue) resText = '%d?%s' % (seq, resCode) resTexts.append(resText) resSeqs.append('%d?' % seq) resCodes.add(resCode) if len(resCodes) == 1: residueText = '/'.join(resSeqs) + resCodes.pop() else: residueText = '/'.join(resTexts) elif spinSystem.ccpCode: getResidueCode(spinSystem) shifts = [] if self.shiftList: for resonance in spinSystem.resonances: if resonance.isotopeCode in self.isotopes: shift = resonance.findFirstShift(parentList = self.shiftList) if shift: shifts.append('%.2f' % shift.value) shifts.sort() shiftsText = ' '.join(shifts) data = [] data.append(spinSystem.serial) data.append(residueText) data.append(includeText) data.append(scoreText) data.append(typesText) data.append(shiftsText) objectList.append(spinSystem) textMatrix.append(data) self.scrolledMatrix.update(textMatrix=textMatrix, objectList=objectList) self.updateButtons() self.waiting = False def getChains(self): chains = [] if self.project: for molSystem in self.project.sortedMolSystems(): for chain in molSystem.sortedChains(): if chain.molecule.molType in ('protein',None): # None moltype may be mixed, including protein component text = '%s:%s' % (molSystem.code, chain.code) chains.append( [text, chain] ) return chains def changeChain(self, chain): self.chain = chain self.updateSpinSystemsAfter() def updateChains(self, *chain): data = self.getChains() names = [x[0] for x in data] chains = [x[1] for x in data] index = 0 if chains: if self.chain not in chains: if self.spinSystem: if self.spinSystem.residue: self.chain = self.spinSystem.residue.chain else: self.chain = chains[0] index = chains.index(self.chain) self.chainPulldown.setup(names, chains, index) self.updateButtons() def run(self): spinSystems = [] for spinSystem in self.scrolledMatrix.objectList: if spinSystem.sstSelected == 'Yes': spinSystems.append(spinSystem) if self.chain and spinSystems: if self.progressBar: self.progressBar.destroy() self.progressBar = TypingEnsemblePopup(self,total=100) residues = self.chain.sortedResidues() numBest = self.ensembleEntry.get() or 20 numSteps = max(100, self.stepsEntry.get() or 100000) graph = self.progressBar.graph typeScores, cc0 = getSpinSystemTypes(residues, spinSystems, self.preserveTypes, isotopes=self.isotopes, shiftList=self.shiftList, numSteps=numSteps, numBest=numBest, graph=graph, progressBar=self.progressBar) threshold = self.thresholdEntry.get() for ss in typeScores.keys(): ss.sstTypes = [] ss.ssScore = None for ccpCode in typeScores[ss].keys(): if ccpCode and typeScores[ss][ccpCode] > threshold: if ccpCode not in ss.sstTypes: ss.sstTypes.append(ccpCode) if len(ss.sstTypes) == 1: ss.ssScore = typeScores[ss][ss.sstTypes[0]] self.updateSpinSystemsAfter() def destroy(self): for func in ('__init__','delete'): self.unregisterNotify(self.updateChains, 'ccp.molecule.MolSystem.Chain', func) self.unregisterNotify(self.updateShiftLists, 'ccp.nmr.Nmr.ShiftList', func) for func in ('__init__','delete','setCcpCode', 'setResidue','setName','addResonance', 'removeResonance','setResonances'): self.unregisterNotify(self.updateSpinSystemsAfter, 'ccp.nmr.Nmr.ResonanceGroup', func) if self.scoresPopup: self.scoresPopup.destroy() BasePopup.destroy(self)
class AssignMentTransferTab(object): '''the tab in the GUI where assignments can be transferred in bulk to the ccpn analysis project. A difference is made between two types of assignments: 1) spin systems to residues, which also implies resonanceSets to atomSets. 2) resonances to peak dimensions. The user is able to configure which assignments should be transferred to the project. Attributes: guiParent: gui object this tab is part of. frame: the frame in which this element lives. dataModel(src.cython.malandro.DataModel): dataModel object describing the assignment proposed by the algorithm. selectedSolution (int): The index of the solution/run that is used asa the template to make the assignments. resonanceToDimension (bool): True if resonances should be assigned to peak dimensions. False if not. spinSystemToResidue (bool): True if spin system to residue assignment should be carried out. minScore (float): The minimal score of a spin system assignment to a residue to be allowed to transfer this assignment to the project intra (bool): True if intra-residual peaks should be assigned. sequential (bool): True if sequential peaks should be assigned. noDiagonal (bool): If True, purely diagonal peaks are ignored during the transfer of assignments. allSpectra (bool): If True, all spectra will be assigned. If False, one specified spectrum will be assigned. spectrum (src.cython.malandro.Spectrum): The spectrum that should be assigned. ''' def __init__(self, parent, frame): '''Init. args: parent: the guiElement that this tab is part of. frame: the frame this part of the GUI lives in. ''' self.guiParent = parent self.frame = frame # Buttons and fields, # will be set in body(): self.peaksCheckButton = None self.residuesCheckButton = None self.intraCheckButton = None self.sequentialCheckButton = None self.noDiagonalCheckButton = None self.spinSystemTypeSelect = None self.minScoreEntry = None self.solutionNumberEntry = None self.spectrumSelect = None self.spectraPullDown = None self.assignedResidueStrategySelect = None self.transferButton = None # Settings that determine how assignments # are transferred to the analysis project: self.minScore = 80.0 self.dataModel = None self.spectrum = None self.selectedSolution = 1 self.body() self.resonanceToDimension = True self.spinSystemToResidue = True self.intra = True self.sequential = True self.noDiagonal = True self.allSpectra = True self.spinSystemType = 0 self.strategy = 0 def body(self): '''Describes the body of this tab. It consists out of a number of radio buttons, check buttons and number entries that allow the user to indicate which assignments should be transferred. ''' # self.frame.expandColumn(0) self.frame.expandGrid(8, 0) self.frame.expandGrid(8, 1) typeOfAssignmentFrame = LabelFrame(self.frame, text='type of assignment') typeOfAssignmentFrame.grid(row=0, column=0, sticky='nesw') # typeOfAssignmentFrame.expandGrid(0,5) peakSelectionFrame = LabelFrame(self.frame, text='which peaks to assign') peakSelectionFrame.grid(row=0, column=1, sticky='nesw', rowspan=2) spinSystemSelectionFrame = LabelFrame(self.frame, text='Which spin-systems to use') spinSystemSelectionFrame.grid(row=2, column=0, sticky='nesw') tipText = 'What to do when a residue has already a spin system assigned to it.' assignedResidueFrame = LabelFrame( self.frame, text='if residue already has spin-system', tipText=tipText) assignedResidueFrame.grid(row=2, column=1, sticky='nesw') spectrumSelectionFrame = LabelFrame(self.frame, text='spectra') spectrumSelectionFrame.grid(row=1, column=0, sticky='nesw') row = 0 Label(typeOfAssignmentFrame, text='Resonances to Peak Dimensions', grid=(row, 0)) self.peaksCheckButton = CheckButton(typeOfAssignmentFrame, selected=True, grid=(row, 1)) row += 1 Label(typeOfAssignmentFrame, text='SpinSystems to Residues', grid=(row, 0)) self.residuesCheckButton = CheckButton(typeOfAssignmentFrame, selected=True, grid=(row, 1)) row = 0 Label(peakSelectionFrame, text='Intra-Residual', grid=(row, 0)) self.intraCheckButton = CheckButton(peakSelectionFrame, selected=True, grid=(row, 1)) row += 1 Label(peakSelectionFrame, text='Sequential', grid=(row, 0)) self.sequentialCheckButton = CheckButton(peakSelectionFrame, selected=True, grid=(row, 1)) row += 1 Label(peakSelectionFrame, text='Do not assign diagonal peaks', grid=(row, 0)) self.noDiagonalCheckButton = CheckButton(peakSelectionFrame, selected=True, grid=(row, 1)) entries = [ 'Only assigned spin systems', 'All that have a score of at least: ', 'User Defined', 'Solution number:' ] tipTexts = [ 'Only assign resonances of spin systems that already have a sequential assignment for the assignment of peak dimensions. Spin system to residue assignment is not relevant in this case.', 'Assign all spin systems that have a score of at least a given percentage. 50% or lower is not possible, because than spin systems might have to be assigned to more than 1 residue, which is impossible.', "As defined in the lower row of buttons in the 'results' tab.", 'One of the single solutions of the annealing.' ] self.spinSystemTypeSelect = RadioButtons(spinSystemSelectionFrame, entries=entries, grid=(0, 0), select_callback=None, direction=VERTICAL, gridSpan=(4, 1), tipTexts=tipTexts) tipText = 'The minimal amount of colabelling the different nuclei should have in order to still give rise to a peak.' self.minScoreEntry = FloatEntry(spinSystemSelectionFrame, grid=(1, 1), width=7, text=str(self.minScore), returnCallback=self.changeMinScore, tipText=tipText) self.minScoreEntry.bind('<Leave>', self.changeMinScore, '+') self.solutionNumberEntry = IntEntry(spinSystemSelectionFrame, grid=(3, 1), width=7, text=1, returnCallback=self.solutionUpdate, tipText=tipText) self.solutionNumberEntry.bind('<Leave>', self.solutionUpdate, '+') #self.solutionPullDown = PulldownList(spinSystemSelectionFrame, None, grid=(3,1), sticky='w') entries = ['all spectra', 'only:'] tipTexts = [ 'Assign peaks in all the spectra that where selected before the annealing ran.', 'Only assign peaks in one particular spectrum. You can of course repeat this multiple times for different spectra.' ] self.spectrumSelect = RadioButtons(spectrumSelectionFrame, entries=entries, grid=(0, 0), select_callback=None, direction=VERTICAL, gridSpan=(2, 1), tipTexts=tipTexts) self.spectraPullDown = PulldownList(spectrumSelectionFrame, self.changeSpectrum, grid=(1, 1), sticky='w') entries = [ 'skip this residue', 'de-assign old spin system from residue', 'assign, but never merge', 'warn to merge' ] tipTexts = [ "Don't assign the new spin system to the residue. The residue is not skipped when the old spin system does not contain any resonances", "De-assign old spin system from residue, unless the old spin system is a spin system without any resonances.", "Don't merge any spin systems, merging can be performed later if nescesary in the Resonance --> SpinSystems window.", "Ask to merge individually for each spin system, this might result in clicking on a lot of popups." ] self.assignedResidueStrategySelect = RadioButtons(assignedResidueFrame, entries=entries, grid=(0, 0), select_callback=None, direction=VERTICAL, gridSpan=(2, 1), tipTexts=tipTexts) texts = ['Transfer Assignments'] commands = [self.transferAssignments] self.transferButton = ButtonList(self.frame, commands=commands, texts=texts) self.transferButton.grid(row=5, column=0, sticky='nsew', columnspan=2) def update(self): '''Update the nescesary elements in the tab. Is called when the algorithm has produced possible assignments. The only thing that has to be updated in practice in this tab is the pulldown with spectra. ''' self.dataModel = self.guiParent.connector.results self.updateSpectra() def setDataModel(self, dataModel): '''Here the dataModel, which is the dataModel containing the suggested assignments body the algorithm, can be set. ''' self.dataModel = dataModel self.update() def updateSpectra(self, *opt): '''Updates the spectra shown in the spectra pulldown. These are only the spectra that were used by the algorithm. All other spectra in the project are not relevant since for those no simulated peaks have been matched to real peaks. ''' if not self.dataModel: return spectrum = self.spectrum spectra = self.dataModel.getSpectra() if spectra: names = [spectrum.name for spectrum in spectra] index = 0 if self.spectrum not in spectra: self.spectrum = spectra[0] else: index = spectra.index(self.spectrum) self.spectraPullDown.setup(names, spectra, index) def changeSpectrum(self, spectrum): '''Select a spectum to be assigned.''' self.spectrum = spectrum def solutionUpdate(self, event=None, value=None): '''Select a solution. A solution is a one to one mapping of spin systems to residues produced by one run of the algorithm. args: event: event object, this is one of the values the number entry calls his callback function with. value: the index of the solution/run. ''' if not self.dataModel: return Nsolutions = len(self.dataModel.chain.residues[0].solutions) if value is None: value = self.solutionNumberEntry.get() if value == self.selectedSolution: return else: self.selectedSolution = value if value < 1: self.solutionNumberEntry.set(1) self.selectedSolution = 1 elif value > Nsolutions: self.selectedSolution = Nsolutions self.solutionNumberEntry.set(self.selectedSolution) else: self.solutionNumberEntry.set(self.selectedSolution) def fetchOptions(self): '''Fetches user set options from the gui in one go and stores them in their corresponding instance variables. ''' self.resonanceToDimension = self.peaksCheckButton.get() self.spinSystemToResidue = self.residuesCheckButton.get() self.intra = self.intraCheckButton.get() self.sequential = self.sequentialCheckButton.get() self.noDiagonal = self.noDiagonalCheckButton.get() self.spinSystemType = self.spinSystemTypeSelect.getIndex() self.strategy = ['skip', 'remove', 'noMerge', None][self.assignedResidueStrategySelect.getIndex()] self.allSpectra = [True, False][self.spectrumSelect.getIndex()] def changeMinScore(self, event=None): '''Set the minimal score for which a spin system to residue assignment gets transferred to the ccpn analysis project. ''' newMinScore = self.minScoreEntry.get() if self.minScore != newMinScore: if newMinScore <= 50.0: self.minScore = 51.0 self.minScoreEntry.set(51.0) elif newMinScore > 100.0: self.minScore = 100.0 self.minScoreEntry.set(100.0) else: self.minScore = newMinScore def transferAssignments(self): '''Transfer assignments to project depending on the settings from the GUI. ''' self.fetchOptions() if not self.dataModel or (not self.resonanceToDimension and not self.spinSystemToResidue): return strategy = self.strategy lookupSpinSystem = [ self.getAssignedSpinSystem, self.getBestScoringSpinSystem, self.getUserDefinedSpinSystem, self.getSelectedSolutionSpinSystem ][self.spinSystemType] residues = self.dataModel.chain.residues spinSystemSequence = [lookupSpinSystem(res) for res in residues] ccpnSpinSystems = [] ccpnResidues = [] # if self.spinSystemType == 0 it means that it for sure already # assigned like this if self.spinSystemToResidue and not self.spinSystemType == 0: for spinSys, res in zip(spinSystemSequence, residues): if spinSys and res: ccpnSpinSystems.append(spinSys.getCcpnResonanceGroup()) ccpnResidues.append(res.getCcpnResidue()) assignSpinSystemstoResidues(ccpnSpinSystems, ccpnResidues, strategy=strategy, guiParent=self.guiParent) if self.resonanceToDimension: allSpectra = self.allSpectra if self.intra: for residue, spinSystem in zip(residues, spinSystemSequence): if not spinSystem: continue intraLink = residue.getIntraLink(spinSystem) for pl in intraLink.getPeakLinks(): peak = pl.getPeak() if not allSpectra and peak.getSpectrum( ) is not self.spectrum: continue if not peak: continue resonances = pl.getResonances() if self.noDiagonal and len( set(resonances)) < len(resonances): continue for resonance, dimension in zip( resonances, peak.getDimensions()): ccpnResonance = resonance.getCcpnResonance() ccpnDimension = dimension.getCcpnDimension() assignResToDim(ccpnDimension, ccpnResonance) if self.sequential: for residue, spinSystemA, spinSystemB in zip( residues, spinSystemSequence, spinSystemSequence[1:]): if not spinSystemA or not spinSystemB: continue link = residue.getLink(spinSystemA, spinSystemB) for pl in link.getPeakLinks(): peak = pl.getPeak() if not allSpectra and peak.getSpectrum( ) is not self.spectrum: continue if not peak: continue resonances = pl.getResonances() if self.noDiagonal and len( set(resonances)) < len(resonances): continue for resonance, dimension in zip( resonances, peak.getDimensions()): ccpnResonance = resonance.getCcpnResonance() ccpnDimension = dimension.getCcpnDimension() assignResToDim(ccpnDimension, ccpnResonance) self.guiParent.resultsTab.update() def getAssignedSpinSystem(self, residue): '''Get the spinSystem that is assigned in the project to a residue. args: residue (src.cython.malandro.Residue) return: spinSystem (src.cython.malandro.SpinSystem) ''' ccpCode = residue.ccpCode seqCode = residue.getSeqCode() spinSystems = self.dataModel.getSpinSystems()[ccpCode] ccpnResidue = residue.getCcpnResidue() if ccpnResidue: assignedResonanceGroups = ccpnResidue.getResonanceGroups() if len(assignedResonanceGroups) > 1: print 'There is more than one spin system assigned to residue %s, did not know which one to use to assign peaks. Therefor this residue is skipped.' % ( seqCode) return assignedResonanceGroup = ccpnResidue.findFirstResonanceGroup() if assignedResonanceGroup: for spinSystem in spinSystems: if spinSystem.getSerial() == assignedResonanceGroup.serial: # Just checking to make sure, analysis project could # have changed if not self.skipResidue(residue, spinSystem): return spinSystem def getBestScoringSpinSystem(self, residue): '''Get the spinSystem that scores the highest, i.e. is assigned in most of the runs to the given residue. args: residue (src.cython.malandro.Residue) return: spinSystem (src.cython.malandro.SpinSystem) ''' solutions = residue.solutions weigth = 1.0 / len(solutions) score, bestSpinSystem = max([ (solutions.count(solution) * weigth * 100.0, solution) for solution in solutions ]) if score >= self.minScore and not bestSpinSystem.getIsJoker( ) and not self.skipResidue(residue, bestSpinSystem): return bestSpinSystem return None def getUserDefinedSpinSystem(self, residue): '''Get the spinSystem that is defined by the user (probably in the resultsTab) as the correct assignment of the given residue. args: residue (src.cython.malandro.Residue) return: spinSystem (src.cython.malandro.SpinSystem) ''' userDefinedSpinSystem = residue.userDefinedSolution if userDefinedSpinSystem and not userDefinedSpinSystem.getIsJoker( ) and not self.skipResidue(residue, userDefinedSpinSystem): return userDefinedSpinSystem return None def getSelectedSolutionSpinSystem(self, residue): '''I a solution corresponding to one specific run of the algorithm is defined, return which spinSystem in that run got assigned to the given residue. args: residue (src.cython.malandro.Residue) return: spinSystem (src.cython.malandro.SpinSystem) ''' solutions = residue.solutions spinSystem = solutions[self.selectedSolution - 1] if not spinSystem.getIsJoker() and not self.skipResidue( residue, spinSystem): return spinSystem return None def skipResidue(self, residue, spinSystem): '''One strategy is to skip all residues that already have a spin system assignment. If that is the case determine whether to skip the given residue. args: residue (src.cython.malandro.Residue) spinSystem (src.cython.malandro.SpinSystem) return: boolean, True if residue should be skipped. ''' if self.strategy == 0: assignedGroups = residue.getCcpnResidue().getResonanceGroups() assignedSerials = set( [spinSys.serial for spinSys in assignedGroups]) if assignedSerials and spinSystem.getSerial( ) not in assignedSerials: return True return False
class NmrPipePseudoPopup(BasePopup): pseudoEntries = ('Is Pseudo Expt', 'Is Not Pseudo Expt') def __init__(self, parent, params, dim, fileName='', *args, **kw): self.dim = dim self.params = params self.fileName = fileName m = template_re.match(fileName) if m: n = len(m.groups(2)) ss = '%%0%dd' % n template = re.sub(template_re, r'\1%s\3' % ss, fileName) else: template = fileName self.template = template BasePopup.__init__(self, parent=parent, title='NMRPipe Pseudo Data', modal=True, **kw) def body(self, master): fileName = self.fileName directory = os.path.dirname(fileName) if not directory: directory = os.getcwd() fileName = os.path.basename(fileName) m = template_re.match(fileName) if m: n = len(m.groups(2)) ss = '%%0%dd' % n template = re.sub(template_re, r'\1%s\3' % ss, fileName) else: template = fileName master.rowconfigure(0, weight=1) master.rowconfigure(1, weight=1) master.columnconfigure(1, weight=1) tipTexts = [ 'The experiment is pseudo-N dimensional, with a sampled axis', 'The experiment is the regular kind with only NMR frequency axes' ] self.pseudoButton = RadioButtons( master, entries=self.pseudoEntries, select_callback=self.changedPseudoMode, grid=(0, 0), sticky='nw', tipTexts=tipTexts) frame = self.pseudoFrame = Frame(master) self.pseudoFrame.grid(row=1, column=0, sticky='nsew') row = 0 npts = self.params.npts[self.dim] tipText = 'Number of data points (planes) along sampled axis' label = Label(frame, text='Number of points: ') label.grid(row=row, column=0, sticky='e') self.nptsEntry = IntEntry(frame, text=npts, tipText=tipText, width=8, grid=(row, 1)) tipText = 'Load the values for the sampled axis from a text file containing a list of numeric values' Button(frame, text='Load values from text file', command=self.loadValues, tipText=tipText, grid=(row, 2), sticky='ew') row = row + 1 tipText = 'The values (e.g. T1, T2) corresponding to each data point (plane) along sampled axis' label = Label(frame, text='Point values: ') label.grid(row=row, column=0, sticky='e') self.valueEntry = FloatEntry(frame, isArray=True, tipText=tipText) self.valueEntry.grid(row=row, column=1, columnspan=2, sticky='ew') row = row + 1 tipText = 'Fetch the Point values from the files given by the NMRPipe template' button = Button( frame, text='Fetch values from file(s) specified by template below', command=self.fetchValues, tipText=tipText) button.grid(row=row, column=1, columnspan=2, sticky='w') row = row + 1 tipText = 'The directory where the data files reside' button = Button(frame, text='Data directory: ', command=self.chooseDirectory) button.grid(row=row, column=0, sticky='e') self.directoryEntry = Entry(frame, text=directory, width=40, tipText=tipText) self.directoryEntry.grid(row=row, column=1, columnspan=2, sticky='ew') row = row + 1 tipText = 'The NMRPipe template for the data files, if you want to use these to fetch the point values from' button = Button(frame, text='NMRPipe template: ', command=self.chooseFile) button.grid(row=row, column=0, sticky='e') self.templateEntry = Entry(frame, text=template, tipText=tipText) self.templateEntry.grid(row=row, column=1, columnspan=2, sticky='ew') for n in range(row): frame.rowconfigure(n, weight=1) frame.columnconfigure(1, weight=1) buttons = UtilityButtonList(master, closeText='Ok', doClone=False, closeCmd=self.updateParams, helpUrl=self.help_url) buttons.grid(row=2, column=0, sticky='ew') def loadValues(self): directory = self.parent.fileSelect.getDirectory() fileSelectPopup = FileSelectPopup(self, title='Select Sampled Data File', dismiss_text='Cancel', selected_file_must_exist=True, multiSelect=False, directory=directory) fileName = fileSelectPopup.file_select.getFile() if not fileName: return fileObj = open(fileName, 'rU') data = '' line = fileObj.readline() while line: data += line line = fileObj.readline() fileObj.close() data = re.sub(',\s+', ',', data) data = re.sub('\s+', ',', data) data = re.sub(',,', ',', data) data = re.sub('[^0-9,.\-+eE]', '', data) self.valueEntry.set(data) def chooseDirectory(self): directory = os.path.dirname(self.fileName) if not directory: directory = os.getcwd() popup = FileSelectPopup(self, directory=directory, show_file=False) directory = popup.getDirectory() popup.destroy() if directory: self.directoryEntry.set(directory) def chooseFile(self): directory = self.directoryEntry.get() if not directory: directory = os.getcwd() popup = FileSelectPopup(self, directory=directory) file = popup.getFile() popup.destroy() if file: template = os.path.basename(file) self.templateEntry.set(template) def updateParams(self): params = self.params if self.pseudoButton.get() == self.pseudoEntries[0]: npts = self.nptsEntry.get() params.npts[self.dim] = npts values = self.valueEntry.get() try: params.setSampledDim(self.dim, values) except ApiError, e: showError('Set Sampled Dim', e.error_msg, parent=self) return params.fixNullDims(ignoreDim=self.dim) else:
class PopupTemplate(BasePopup): def __init__(self, parent, project=None, *args, **kw): self.project = project self.parent = parent self.objects = self.getObjects() self.object = None BasePopup.__init__(self, parent=parent, title='Popup Template', **kw) self.updateObjects() def body(self, mainFrame): mainFrame.grid_columnconfigure(1, weight=1, minsize=100) mainFrame.config(borderwidth=5, relief='solid') row = 0 label = Label(mainFrame, text="Frame (with sub-widgets):") label.grid(row=row, column=0, sticky=Tkinter.E) frame = Frame(mainFrame, relief='raised', border=2, background='#8080D0') # Frame expands East-West frame.grid(row=row, column=1, sticky=Tkinter.EW) # Last column expands => Widgets pusted to the West frame.grid_columnconfigure(3, weight=1) # Label is within the sub frame label = Label(frame, text='label ') label.grid(row=0, column=0, sticky=Tkinter.W) entry = Entry(frame, text='Entry', returnCallback=self.showWarning) entry.grid(row=0, column=1, sticky=Tkinter.W) self.check = CheckButton(frame, text='Checkbutton', selected=True, callback=self.updateObjects) self.check.grid(row=0, column=2, sticky=Tkinter.W) # stick a button to the East wall button = Button(frame, text='Button', command=self.pressButton) button.grid(row=0, column=3, sticky=Tkinter.E) row += 1 label = Label(mainFrame, text="Text:") label.grid(row=row, column=0, sticky=Tkinter.E) self.textWindow = Text(mainFrame, text='Initial Text\n', width=60, height=5) self.textWindow.grid(row=row, column=1, sticky=Tkinter.NSEW) row += 1 label = Label(mainFrame, text="CheckButtons:") label.grid(row=row, column=0, sticky=Tkinter.E) entries = ['Alpha','Beta','Gamma','Delta'] selected = entries[2:] self.checkButtons = CheckButtons(mainFrame, entries, selected=selected,select_callback=self.changedCheckButtons) self.checkButtons.grid(row=row, column=1, sticky=Tkinter.W) row += 1 label = Label(mainFrame, text="PartitionedSelector:") label.grid(row=row, column=0, sticky=Tkinter.E) labels = ['Bool','Int','Float','String'] objects = [type(0),type(1),type(1.0),type('a')] selected = [type('a')] self.partitionedSelector= PartitionedSelector(mainFrame, labels=labels, objects=objects, colors = ['red','yellow','green','#000080'], callback=self.toggleSelector,selected=selected) self.partitionedSelector.grid(row=row, column=1, sticky=Tkinter.EW) row += 1 label = Label(mainFrame, text="PulldownMenu") label.grid(row=row, column=0, sticky=Tkinter.E) entries = ['Frodo','Pipin','Merry','Sam','Bill','Gandalf','Strider','Gimli','Legolas'] self.pulldownMenu = PulldownMenu(mainFrame, callback=self.selectPulldown, entries=entries, selected_index=2, do_initial_callback=False) self.pulldownMenu.grid(row=row, column=1, sticky=Tkinter.W) row += 1 label = Label(mainFrame, text="RadioButtons in a\nScrolledFrame.frame:") label.grid(row=row, column=0, sticky=Tkinter.EW) frame = ScrolledFrame(mainFrame, yscroll = False, doExtraConfig = True, width=100) frame.grid(row=row, column=1, sticky=Tkinter.EW) frame.grid_columnconfigure(0, weight=1) self.radioButtons = RadioButtons(frame.frame, entries=entries, select_callback=self.checkRadioButtons, selected_index=1, relief='groove') self.radioButtons.grid(row=0, column=0, sticky=Tkinter.W) row += 1 label = Label(mainFrame, text="LabelFrame with\nToggleLabels inside:") label.grid(row=row, column=0, sticky=Tkinter.E) labelFrame = LabelFrame(mainFrame, text='Frame Title') labelFrame.grid(row=row, column=1, sticky=Tkinter.NSEW) labelFrame.grid_rowconfigure(0, weight=1) labelFrame.grid_columnconfigure(3, weight=1) self.toggleLabel1 = ToggleLabel(labelFrame, text='ScrolledMatrix', callback=self.toggleFrame1) self.toggleLabel1.grid(row=0, column=0, sticky=Tkinter.W) self.toggleLabel1.arrowOn() self.toggleLabel2 = ToggleLabel(labelFrame, text='ScrolledGraph', callback=self.toggleFrame2) self.toggleLabel2.grid(row=0, column=1, sticky=Tkinter.W) self.toggleLabel3 = ToggleLabel(labelFrame, text='ScrolledCanvas', callback=self.toggleFrame3) self.toggleLabel3.grid(row=0, column=2, sticky=Tkinter.W) row += 1 mainFrame.grid_rowconfigure(row, weight=1) label = Label(mainFrame, text="changing/shrinking frames:") label.grid(row=row, column=0, sticky=Tkinter.E) self.toggleRow = row self.toggleFrame = Frame(mainFrame) self.toggleFrame.grid(row=row, column=1, sticky=Tkinter.NSEW) self.toggleFrame.grid_rowconfigure(0, weight=1) self.toggleFrame.grid_columnconfigure(0, weight=1) # option 1 self.intEntry = IntEntry(self, returnCallback = self.setNumber, width=8) self.multiWidget = MultiWidget(self, Entry, options=None, values=None, callback=self.setKeywords, minRows=3, maxRows=5) editWidgets = [None, None, self.intEntry, self.multiWidget] editGetCallbacks = [None, None, self.getNumber, self.getKeywords] editSetCallbacks = [None, None, self.setNumber, self.setKeywords] headingList = ['Name','Color','Number','Keywords'] self.scrolledMatrix = ScrolledMatrix(self.toggleFrame, headingList=headingList, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, callback=self.selectObject, multiSelect=False) self.scrolledMatrix.grid(row=0, column=0, sticky=Tkinter.NSEW) # option 2 self.scrolledGraph = ScrolledGraph(self.toggleFrame, width=400, height=300, symbolSize=5, symbols=['square','circle'], dataColors=['#000080','#800000'], lineWidths=[0,1] ) self.scrolledGraph.setZoom(1.3) dataSet1 = [[0,0],[1,1],[2,4],[3,9],[4,16],[5,25]] dataSet2 = [[0,0],[1,3],[2,6],[3,9],[4,12],[5,15]] self.scrolledGraph.update(dataSets=[dataSet1,dataSet2], xLabel = 'X axis label', yLabel = 'Y axis label', title = 'Main Title') self.scrolledGraph.draw() # option 3 self.scrolledCanvas = ScrolledCanvas(self.toggleFrame,relief = 'groove', borderwidth = 2, resizeCallback=None) canvas = self.scrolledCanvas.canvas font = 'Helvetica 10' box = canvas.create_rectangle(10,10,150,200, outline='grey', fill='grey90') line = canvas.create_line(0,0,200,200,fill='#800000', width=2) text = canvas.create_text(120,50, text='Text', font=font, fill='black') circle = canvas.create_oval(30,30,50,50,outline='#008000',fill='#404040',width=3) row += 1 label = Label(mainFrame, text="FloatEntry:") label.grid(row=row, column=0, sticky=Tkinter.E) self.floatEntry = FloatEntry(mainFrame, text=3.14159265, returnCallback=self.floatEntryReturn) self.floatEntry.grid(row=row, column=1, sticky=Tkinter.W) row += 1 label = Label(mainFrame, text="Scale:") label.grid(row=row, column=0, sticky=Tkinter.E) self.scale = Scale(mainFrame, from_=10, to=90, value=50, orient=Tkinter.HORIZONTAL) self.scale.grid(row=row, column=1, sticky=Tkinter.W) row += 1 label = Label(mainFrame, text="Value Ramp:") label.grid(row=row, column=0, sticky=Tkinter.E) self.valueRamp = ValueRamp(mainFrame, self.valueRampCallback, speed = 1.5, delay = 50) self.valueRamp.grid(row=row, column=1, sticky=Tkinter.W) row += 1 label = Label(mainFrame, text="ButtonList:") label.grid(row=row, column=0, sticky=Tkinter.E) texts = ['Select File','Close','Quit'] commands = [self.selectFile, self.close, self.quit] bottomButtons = ButtonList(mainFrame, texts=texts, commands=commands, expands=True) bottomButtons.grid(row=row, column=1, sticky=Tkinter.EW) self.protocol('WM_DELETE_WINDOW', self.quit) def floatEntryReturn(self, event): value = self.floatEntry.get() self.textWindow.setText('%s\n' % value) def selectObject(self, object, row, col): self.object = object def getKeywords(self, object): if object : values = object.keywords self.multiWidget.set(values) def setKeywords(self, event): values = self.multiWidget.get() self.object.keywords = values self.updateObjects() def getNumber(self, object): if object : self.intEntry.set(object.quantity) def setNumber(self, event): value = self.intEntry.get() self.object.quantity = value self.updateObjects() def toggleFrame1(self, isHidden): if isHidden: self.scrolledMatrix.grid_forget() self.toggleFrame.grid_forget() else: self.scrolledGraph.grid_forget() self.scrolledCanvas.grid_forget() self.scrolledMatrix.grid(row=0, column=0, sticky=Tkinter.NSEW) self.toggleFrame.grid(row=self.toggleRow, column=1,sticky=Tkinter.NSEW) self.toggleLabel2.arrowOff() self.toggleLabel3.arrowOff() def toggleFrame2(self, isHidden): if isHidden: self.scrolledGraph.grid_forget() self.toggleFrame.grid_forget() else: self.scrolledMatrix.grid_forget() self.scrolledCanvas.grid_forget() self.scrolledGraph.grid(row=0, column=0, sticky=Tkinter.NSEW) self.toggleFrame.grid(row=self.toggleRow, column=1,sticky=Tkinter.NSEW) self.toggleLabel1.arrowOff() self.toggleLabel3.arrowOff() def toggleFrame3(self, isHidden): if isHidden: self.scrolledCanvas.grid_forget() self.toggleFrame.grid_forget() else: self.scrolledMatrix.grid_forget() self.scrolledGraph.grid_forget() self.scrolledCanvas.grid(row=0, column=0, sticky=Tkinter.NSEW) self.toggleFrame.grid(row=self.toggleRow, column=1,sticky=Tkinter.NSEW) self.toggleLabel1.arrowOff() self.toggleLabel2.arrowOff() def valueRampCallback(self, value): self.textWindow.setText('%s\n' % value) def checkRadioButtons(self, value): self.textWindow.setText('%s\n' % value) def selectPulldown(self, index, name): self.textWindow.setText('%d, %s\n' % (index, name)) def toggleSelector(self, value): self.textWindow.setText('%s\n' % value) def changedCheckButtons(self, values): self.textWindow.setText(','.join(values) + '\n') def getObjects(self): objects = [] objects.append( Fruit('Lemon', '#FFFF00',1,keywords=['Bitter','Tangy'] ) ) objects.append( Fruit('Orange', '#FF8000',4 ) ) objects.append( Fruit('Banana', '#FFF000',5 ) ) objects.append( Fruit('Pinapple','#FFD000',9 ) ) objects.append( Fruit('Kiwi', '#008000',12) ) objects.append( Fruit('Lime', '#00FF00',2 ) ) objects.append( Fruit('Apple', '#800000',5,keywords=['Crunchy'] ) ) objects.append( Fruit('Pear', '#408000',6 ) ) objects.append( Fruit('Peach', '#FFE0C0',2,keywords=['Sweet','Furry'] ) ) objects.append( Fruit('Plumb', '#800080',7 ) ) return objects def updateObjects(self, event=None): textMatrix = [] objectList = [] colorMatrix = [] for object in self.objects: datum = [] datum.append( object.name ) datum.append( None ) datum.append( object.quantity ) datum.append( ','.join(object.keywords) ) colors = [None, object.color, None, None] textMatrix.append(datum) objectList.append(object) colorMatrix.append(colors) if self.check.get(): self.scrolledMatrix.update(textMatrix=textMatrix, objectList=objectList) else: self.scrolledMatrix.update(textMatrix=textMatrix, objectList=objectList, colorMatrix=colorMatrix) def selectFile(self): fileSelectPopup = FileSelectPopup(self, title = 'Choose file', dismiss_text = 'Cancel', selected_file_must_exist = True) fileName = fileSelectPopup.getFile() self.textWindow.setText('File Selected: %s\n' % fileName) def showWarning(self, eventObject): self.textWindow.setText('Text Entry Return Pressed\n') showWarning('Warning Title','Warning Message') return def pressButton(self): self.textWindow.setText('Button Pressed\n') if showYesNo('Title','Prompt: Clear text window?'): self.textWindow.clear() return def quit(self): BasePopup.destroy(self)
class SetReferencePopup(BasePopup): def __init__(self, parent, dataDim, position): self.dataDim = dataDim self.position = position BasePopup.__init__(self, parent=parent, title='Set spectrum referencing', modal=True, transient=True) def body(self, guiFrame): dataDim = self.dataDim dataDimRef = getPrimaryDataDimRef(dataDim) spectrum = dataDim.dataSource guiFrame.grid_columnconfigure(0, weight=1) row = 0 label = Label(guiFrame, text='Experiment:', grid=(row, 0), sticky='e') tipText = 'The name of the experiment that contains the spectrum to be re-referenced' label = Label(guiFrame, text=spectrum.experiment.name, grid=(row, 1), tipText=tipText) row += 1 label = Label(guiFrame, text='Spectrum:', grid=(row, 0), sticky='e') tipText = 'The name of the spectrum to be re-referenced' label = Label(guiFrame, text=spectrum.name, grid=(row, 1), tipText=tipText) row += 1 label = Label(guiFrame, text='Dimension:', grid=(row, 0), sticky='e') tipText = 'The number of the spectrum dimension that will be re-referenced' label = Label(guiFrame, text=self.dataDimText(dataDim), grid=(row, 1), tipText=tipText) row += 1 label = Label(guiFrame, text='Chosen reference point:', grid=(row, 0), sticky='e') text = formatFloat(self.position, places=6) tipText = 'The chosen position in the spectrum dimension (where the mouse was clicked) in data matrix points' label = Label(guiFrame, text=text, grid=(row, 1), tipText=tipText) row += 1 label = Label(guiFrame, text='Current reference ppm at this point:', grid=(row, 0), sticky='e') self.current_refppm = convertPosition(self.position, dataDimRef, toUnit='ppm') text = formatFloat(self.current_refppm, places=6) tipText = 'The ppm value currently associated with this points location' label = Label(guiFrame, text=text, grid=(row, 1), tipText=tipText) row += 1 label = Label(guiFrame, text='New reference ppm at this point:', grid=(row, 0), sticky='e') tipText = 'Inputs a new value for the exact ppm position of the selected point; consequently re-referencing the spectrum dimension' self.refppm_entry = FloatEntry(guiFrame, width=30, grid=(row, 1), sticky='ew', tipText=tipText) self.refppm_entry.set(0.0) # default row += 1 guiFrame.grid_rowconfigure(row, weight=1) tipTexts = [ 'The name of experiments within the project containing similar spectrum dimensions', 'The name of the spectrum within the project that may be re-referenced', 'The spectrum dimension to which the re-referencing may apply', 'The current reference point for the spectrum dimension in its spectrum data matrix', 'The current reference ppm value for the spectrum dimension; the value at the reference point', 'Sets whether the spectrum dimension will be re-referenced using the above reference point and ppm value' ] headings = ('Experiment', 'Spectrum', 'Dimension', 'Reference\npoint', 'Reference\nppm', 'Change\nreferencing') editWidgets = editSetCallbacks = 6 * [None] editGetCallbacks = [None, None, None, None, None, self.toggleChangeRef] self.datadim_list = ScrolledMatrix(guiFrame, headingList=headings, initialRows=4, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, grid=(row, 0), gridSpan=(1, 2), tipTexts=tipTexts) tipTexts = [ 'Re-reference the selected spectrum dimensions using the stated ppm value', ] row += 1 texts = ['Commit'] commands = [self.ok] buttons = UtilityButtonList(guiFrame, texts=texts, commands=commands, doClone=False, closeText='Cancel', helpUrl=self.help_url, grid=(row, 0), gridSpan=(1, 2), tipTexts=tipTexts) self.updateDataDimList() def dataDimText(self, dataDim): dataDimRef = getPrimaryDataDimRef(dataDim) if dataDimRef: isotope = '/'.join(dataDimRef.expDimRef.isotopeCodes) else: isotope = 'None' text = str(dataDim.dim) + ' (%s)' % isotope return text def updateDataDimList(self, *extra): textMatrix = [] self.dataDims = [] isotopeCodes = getPrimaryDataDimRef( self.dataDim).expDimRef.isotopeCodes project = self.dataDim.root for experiment in self.nmrProject.experiments: for spectrum in experiment.dataSources: if (isSpectrum(spectrum)): for dataDim in spectrum.sortedDataDims(): if (isinstance(dataDim, Nmr.FreqDataDim)): expDimRef = getPrimaryDataDimRef(dataDim).expDimRef if (expDimRef.isotopeCodes == isotopeCodes): if (not hasattr(dataDim, 'change_ref')): if (dataDim == self.dataDim): dataDim.change_ref = True else: dataDim.change_ref = False text = [] text.append(experiment.name) text.append(spectrum.name) text.append(self.dataDimText(dataDim)) dataDimRef = getPrimaryDataDimRef(dataDim) text.append( formatFloat(dataDimRef.refPoint, places=6)) text.append( formatFloat(dataDimRef.refValue, places=6)) if (dataDim.change_ref): text.append(yes_string) else: text.append(no_string) textMatrix.append(text) self.dataDims.append(dataDim) self.datadim_list.update(objectList=self.dataDims, textMatrix=textMatrix) def toggleChangeRef(self, dataDim): dataDim.change_ref = not dataDim.change_ref self.updateDataDimList() def apply(self): refppm = self.refppm_entry.get() if (refppm is None): showError('No reference', 'Must specify reference ppm.', parent=self) return False delta_refppm = refppm - self.current_refppm for dataDim in self.dataDims: if (dataDim.change_ref): dataDimRef = getPrimaryDataDimRef(dataDim) dataDimRef.refValue = dataDimRef.refValue + delta_refppm del dataDim.change_ref return True
class AssignMentTransferTab(object): '''the tab in the GUI where assignments can be transferred in bulk to the ccpn analysis project. A difference is made between two types of assignments: 1) spin systems to residues, which also implies resonanceSets to atomSets. 2) resonances to peak dimensions. The user is able to configure which assignments should be transferred to the project. Attributes: guiParent: gui object this tab is part of. frame: the frame in which this element lives. dataModel(src.cython.malandro.DataModel): dataModel object describing the assignment proposed by the algorithm. selectedSolution (int): The index of the solution/run that is used asa the template to make the assignments. resonanceToDimension (bool): True if resonances should be assigned to peak dimensions. False if not. spinSystemToResidue (bool): True if spin system to residue assignment should be carried out. minScore (float): The minimal score of a spin system assignment to a residue to be allowed to transfer this assignment to the project intra (bool): True if intra-residual peaks should be assigned. sequential (bool): True if sequential peaks should be assigned. noDiagonal (bool): If True, purely diagonal peaks are ignored during the transfer of assignments. allSpectra (bool): If True, all spectra will be assigned. If False, one specified spectrum will be assigned. spectrum (src.cython.malandro.Spectrum): The spectrum that should be assigned. ''' def __init__(self, parent, frame): '''Init. args: parent: the guiElement that this tab is part of. frame: the frame this part of the GUI lives in. ''' self.guiParent = parent self.frame = frame # Buttons and fields, # will be set in body(): self.peaksCheckButton = None self.residuesCheckButton = None self.intraCheckButton = None self.sequentialCheckButton = None self.noDiagonalCheckButton = None self.spinSystemTypeSelect = None self.minScoreEntry = None self.solutionNumberEntry = None self.spectrumSelect = None self.spectraPullDown = None self.assignedResidueStrategySelect = None self.transferButton = None # Settings that determine how assignments # are transferred to the analysis project: self.minScore = 80.0 self.dataModel = None self.spectrum = None self.selectedSolution = 1 self.body() self.resonanceToDimension = True self.spinSystemToResidue = True self.intra = True self.sequential = True self.noDiagonal = True self.allSpectra = True self.spinSystemType = 0 self.strategy = 0 def body(self): '''Describes the body of this tab. It consists out of a number of radio buttons, check buttons and number entries that allow the user to indicate which assignments should be transferred. ''' # self.frame.expandColumn(0) self.frame.expandGrid(8, 0) self.frame.expandGrid(8, 1) typeOfAssignmentFrame = LabelFrame( self.frame, text='type of assignment') typeOfAssignmentFrame.grid(row=0, column=0, sticky='nesw') # typeOfAssignmentFrame.expandGrid(0,5) peakSelectionFrame = LabelFrame( self.frame, text='which peaks to assign') peakSelectionFrame.grid(row=0, column=1, sticky='nesw', rowspan=2) spinSystemSelectionFrame = LabelFrame(self.frame, text='Which spin-systems to use') spinSystemSelectionFrame.grid(row=2, column=0, sticky='nesw') tipText = 'What to do when a residue has already a spin system assigned to it.' assignedResidueFrame = LabelFrame(self.frame, text='if residue already has spin-system', tipText=tipText) assignedResidueFrame.grid(row=2, column=1, sticky='nesw') spectrumSelectionFrame = LabelFrame(self.frame, text='spectra') spectrumSelectionFrame.grid(row=1, column=0, sticky='nesw') row = 0 Label(typeOfAssignmentFrame, text='Resonances to Peak Dimensions', grid=(row, 0)) self.peaksCheckButton = CheckButton(typeOfAssignmentFrame, selected=True, grid=(row, 1)) row += 1 Label(typeOfAssignmentFrame, text='SpinSystems to Residues', grid=(row, 0)) self.residuesCheckButton = CheckButton( typeOfAssignmentFrame, selected=True, grid=(row, 1)) row = 0 Label(peakSelectionFrame, text='Intra-Residual', grid=(row, 0)) self.intraCheckButton = CheckButton( peakSelectionFrame, selected=True, grid=(row, 1)) row += 1 Label(peakSelectionFrame, text='Sequential', grid=(row, 0)) self.sequentialCheckButton = CheckButton( peakSelectionFrame, selected=True, grid=(row, 1)) row += 1 Label(peakSelectionFrame, text='Do not assign diagonal peaks', grid=(row, 0)) self.noDiagonalCheckButton = CheckButton( peakSelectionFrame, selected=True, grid=(row, 1)) entries = ['Only assigned spin systems', 'All that have a score of at least: ', 'User Defined', 'Solution number:'] tipTexts = ['Only assign resonances of spin systems that already have a sequential assignment for the assignment of peak dimensions. Spin system to residue assignment is not relevant in this case.', 'Assign all spin systems that have a score of at least a given percentage. 50% or lower is not possible, because than spin systems might have to be assigned to more than 1 residue, which is impossible.', "As defined in the lower row of buttons in the 'results' tab.", 'One of the single solutions of the annealing.'] self.spinSystemTypeSelect = RadioButtons(spinSystemSelectionFrame, entries=entries, grid=(0, 0), select_callback=None, direction=VERTICAL, gridSpan=(4, 1), tipTexts=tipTexts) tipText = 'The minimal amount of colabelling the different nuclei should have in order to still give rise to a peak.' self.minScoreEntry = FloatEntry(spinSystemSelectionFrame, grid=(1, 1), width=7, text=str(self.minScore), returnCallback=self.changeMinScore, tipText=tipText) self.minScoreEntry.bind('<Leave>', self.changeMinScore, '+') self.solutionNumberEntry = IntEntry(spinSystemSelectionFrame, grid=(3, 1), width=7, text=1, returnCallback=self.solutionUpdate, tipText=tipText) self.solutionNumberEntry.bind('<Leave>', self.solutionUpdate, '+') #self.solutionPullDown = PulldownList(spinSystemSelectionFrame, None, grid=(3,1), sticky='w') entries = ['all spectra', 'only:'] tipTexts = ['Assign peaks in all the spectra that where selected before the annealing ran.', 'Only assign peaks in one particular spectrum. You can of course repeat this multiple times for different spectra.'] self.spectrumSelect = RadioButtons(spectrumSelectionFrame, entries=entries, grid=(0, 0), select_callback=None, direction=VERTICAL, gridSpan=(2, 1), tipTexts=tipTexts) self.spectraPullDown = PulldownList(spectrumSelectionFrame, self.changeSpectrum, grid=(1, 1), sticky='w') entries = ['skip this residue', 'de-assign old spin system from residue', 'assign, but never merge', 'warn to merge'] tipTexts = ["Don't assign the new spin system to the residue. The residue is not skipped when the old spin system does not contain any resonances", "De-assign old spin system from residue, unless the old spin system is a spin system without any resonances.", "Don't merge any spin systems, merging can be performed later if nescesary in the Resonance --> SpinSystems window.", "Ask to merge individually for each spin system, this might result in clicking on a lot of popups."] self.assignedResidueStrategySelect = RadioButtons(assignedResidueFrame, entries=entries, grid=(0, 0), select_callback=None, direction=VERTICAL, gridSpan=(2, 1), tipTexts=tipTexts) texts = ['Transfer Assignments'] commands = [self.transferAssignments] self.transferButton = ButtonList( self.frame, commands=commands, texts=texts) self.transferButton.grid(row=5, column=0, sticky='nsew', columnspan=2) def update(self): '''Update the nescesary elements in the tab. Is called when the algorithm has produced possible assignments. The only thing that has to be updated in practice in this tab is the pulldown with spectra. ''' self.dataModel = self.guiParent.connector.results self.updateSpectra() def setDataModel(self, dataModel): '''Here the dataModel, which is the dataModel containing the suggested assignments body the algorithm, can be set. ''' self.dataModel = dataModel self.update() def updateSpectra(self, *opt): '''Updates the spectra shown in the spectra pulldown. These are only the spectra that were used by the algorithm. All other spectra in the project are not relevant since for those no simulated peaks have been matched to real peaks. ''' if not self.dataModel: return spectrum = self.spectrum spectra = self.dataModel.getSpectra() if spectra: names = [spectrum.name for spectrum in spectra] index = 0 if self.spectrum not in spectra: self.spectrum = spectra[0] else: index = spectra.index(self.spectrum) self.spectraPullDown.setup(names, spectra, index) def changeSpectrum(self, spectrum): '''Select a spectum to be assigned.''' self.spectrum = spectrum def solutionUpdate(self, event=None, value=None): '''Select a solution. A solution is a one to one mapping of spin systems to residues produced by one run of the algorithm. args: event: event object, this is one of the values the number entry calls his callback function with. value: the index of the solution/run. ''' if not self.dataModel: return Nsolutions = len(self.dataModel.chain.residues[0].solutions) if value is None: value = self.solutionNumberEntry.get() if value == self.selectedSolution: return else: self.selectedSolution = value if value < 1: self.solutionNumberEntry.set(1) self.selectedSolution = 1 elif value > Nsolutions: self.selectedSolution = Nsolutions self.solutionNumberEntry.set(self.selectedSolution) else: self.solutionNumberEntry.set(self.selectedSolution) def fetchOptions(self): '''Fetches user set options from the gui in one go and stores them in their corresponding instance variables. ''' self.resonanceToDimension = self.peaksCheckButton.get() self.spinSystemToResidue = self.residuesCheckButton.get() self.intra = self.intraCheckButton.get() self.sequential = self.sequentialCheckButton.get() self.noDiagonal = self.noDiagonalCheckButton.get() self.spinSystemType = self.spinSystemTypeSelect.getIndex() self.strategy = ['skip', 'remove', 'noMerge', None][ self.assignedResidueStrategySelect.getIndex()] self.allSpectra = [True, False][self.spectrumSelect.getIndex()] def changeMinScore(self, event=None): '''Set the minimal score for which a spin system to residue assignment gets transferred to the ccpn analysis project. ''' newMinScore = self.minScoreEntry.get() if self.minScore != newMinScore: if newMinScore <= 50.0: self.minScore = 51.0 self.minScoreEntry.set(51.0) elif newMinScore > 100.0: self.minScore = 100.0 self.minScoreEntry.set(100.0) else: self.minScore = newMinScore def transferAssignments(self): '''Transfer assignments to project depending on the settings from the GUI. ''' self.fetchOptions() if not self.dataModel or (not self.resonanceToDimension and not self.spinSystemToResidue): return strategy = self.strategy lookupSpinSystem = [self.getAssignedSpinSystem, self.getBestScoringSpinSystem, self.getUserDefinedSpinSystem, self.getSelectedSolutionSpinSystem][self.spinSystemType] residues = self.dataModel.chain.residues spinSystemSequence = [lookupSpinSystem(res) for res in residues] ccpnSpinSystems = [] ccpnResidues = [] # if self.spinSystemType == 0 it means that it for sure already # assigned like this if self.spinSystemToResidue and not self.spinSystemType == 0: for spinSys, res in zip(spinSystemSequence, residues): if spinSys and res: ccpnSpinSystems.append(spinSys.getCcpnResonanceGroup()) ccpnResidues.append(res.getCcpnResidue()) assignSpinSystemstoResidues(ccpnSpinSystems, ccpnResidues, strategy=strategy, guiParent=self.guiParent) if self.resonanceToDimension: allSpectra = self.allSpectra if self.intra: for residue, spinSystem in zip(residues, spinSystemSequence): if not spinSystem: continue intraLink = residue.getIntraLink(spinSystem) for pl in intraLink.getPeakLinks(): peak = pl.getPeak() if not allSpectra and peak.getSpectrum() is not self.spectrum: continue if not peak: continue resonances = pl.getResonances() if self.noDiagonal and len(set(resonances)) < len(resonances): continue for resonance, dimension in zip(resonances, peak.getDimensions()): ccpnResonance = resonance.getCcpnResonance() ccpnDimension = dimension.getCcpnDimension() assignResToDim(ccpnDimension, ccpnResonance) if self.sequential: for residue, spinSystemA, spinSystemB in zip(residues, spinSystemSequence, spinSystemSequence[1:]): if not spinSystemA or not spinSystemB: continue link = residue.getLink(spinSystemA, spinSystemB) for pl in link.getPeakLinks(): peak = pl.getPeak() if not allSpectra and peak.getSpectrum() is not self.spectrum: continue if not peak: continue resonances = pl.getResonances() if self.noDiagonal and len(set(resonances)) < len(resonances): continue for resonance, dimension in zip(resonances, peak.getDimensions()): ccpnResonance = resonance.getCcpnResonance() ccpnDimension = dimension.getCcpnDimension() assignResToDim(ccpnDimension, ccpnResonance) self.guiParent.resultsTab.update() def getAssignedSpinSystem(self, residue): '''Get the spinSystem that is assigned in the project to a residue. args: residue (src.cython.malandro.Residue) return: spinSystem (src.cython.malandro.SpinSystem) ''' ccpCode = residue.ccpCode seqCode = residue.getSeqCode() spinSystems = self.dataModel.getSpinSystems()[ccpCode] ccpnResidue = residue.getCcpnResidue() if ccpnResidue: assignedResonanceGroups = ccpnResidue.getResonanceGroups() if len(assignedResonanceGroups) > 1: print 'There is more than one spin system assigned to residue %s, did not know which one to use to assign peaks. Therefor this residue is skipped.' % (seqCode) return assignedResonanceGroup = ccpnResidue.findFirstResonanceGroup() if assignedResonanceGroup: for spinSystem in spinSystems: if spinSystem.getSerial() == assignedResonanceGroup.serial: # Just checking to make sure, analysis project could # have changed if not self.skipResidue(residue, spinSystem): return spinSystem def getBestScoringSpinSystem(self, residue): '''Get the spinSystem that scores the highest, i.e. is assigned in most of the runs to the given residue. args: residue (src.cython.malandro.Residue) return: spinSystem (src.cython.malandro.SpinSystem) ''' solutions = residue.solutions weigth = 1.0 / len(solutions) score, bestSpinSystem = max([(solutions.count(solution) * weigth * 100.0, solution) for solution in solutions]) if score >= self.minScore and not bestSpinSystem.getIsJoker() and not self.skipResidue(residue, bestSpinSystem): return bestSpinSystem return None def getUserDefinedSpinSystem(self, residue): '''Get the spinSystem that is defined by the user (probably in the resultsTab) as the correct assignment of the given residue. args: residue (src.cython.malandro.Residue) return: spinSystem (src.cython.malandro.SpinSystem) ''' userDefinedSpinSystem = residue.userDefinedSolution if userDefinedSpinSystem and not userDefinedSpinSystem.getIsJoker() and not self.skipResidue(residue, userDefinedSpinSystem): return userDefinedSpinSystem return None def getSelectedSolutionSpinSystem(self, residue): '''I a solution corresponding to one specific run of the algorithm is defined, return which spinSystem in that run got assigned to the given residue. args: residue (src.cython.malandro.Residue) return: spinSystem (src.cython.malandro.SpinSystem) ''' solutions = residue.solutions spinSystem = solutions[self.selectedSolution - 1] if not spinSystem.getIsJoker() and not self.skipResidue(residue, spinSystem): return spinSystem return None def skipResidue(self, residue, spinSystem): '''One strategy is to skip all residues that already have a spin system assignment. If that is the case determine whether to skip the given residue. args: residue (src.cython.malandro.Residue) spinSystem (src.cython.malandro.SpinSystem) return: boolean, True if residue should be skipped. ''' if self.strategy == 0: assignedGroups = residue.getCcpnResidue().getResonanceGroups() assignedSerials = set([spinSys.serial for spinSys in assignedGroups]) if assignedSerials and spinSystem.getSerial() not in assignedSerials: return True return False
class CreateContourFilePopup(BasePopup): """ **Create Contour Files for Project** The purpose of this dialog is to allow the user to create new contour files for the project. Not only the spectrum has to be specified, but also the two dimensions (X, Y) that are going to be contoured. The table specifies the conditions that specify the region being contoured, in terms of what is included and excluded. For now, only one condition (which is an "include" condition) is allowed. This also means that the "Add Condition" and "Delete Condition" buttons are disabled. The contour directory is specified by the program, using the project directory but the file name can be specified by the user. The contour levels used are the ones that are current for the spectrum, as set in the `Contour Levels dialog`_. See also: `Spectrum Contour Files`_, `Add Existing Contour Files`_. .. _`Contour Levels dialog`: EditContourLevelsPopup.html .. _`Spectrum Contour Files`: EditContourFilesPopup.html .. _`Add Existing Contour Files`: AddContourFilePopup.html """ def __init__(self, parent, *args, **kw): self.spectrumConditions = [] self.col = None self.spectrum = None BasePopup.__init__(self, parent=parent, title='New Contour File', **kw) def body(self, master): self.geometry('650x200') master.grid_columnconfigure(1, weight=1) row = 0 frame = Frame(master, grid=(row, 0), gridSpan=(1,2)) label = Label(frame, text='Spectrum: ', grid=(row, 0)) tipText = 'Selects the experiment and spectrum for which to make a contour file' self.expt_spectrum = PulldownList(frame, grid=(row, 1), tipText=tipText, callback=self.update) label = Label(frame, text=' (X-dim, Y-dim): ', grid=(row, 2)) tipText = 'Selects which dimensions (projection) of the spectrum form the X and Y axes of the contour file' self.dim_menu = PulldownList(frame, grid=(row, 3), tipText=tipText, callback=self.updateFile) row = row + 1 master.grid_rowconfigure(row, weight=1) #### for now not editable because only allow one include region ###self.conditionMenu = PulldownMenu(self, entries=('include', 'exclude'), ### callback=self.selectedCondition, do_initial_callback=False) self.regionEntry = FloatEntry(self, text='', returnCallback=self.setRegion, width=10) tipTexts = ['Whether to include or exclude the region in the contour file', '', '', ''] headingList = ['Condition','','','',''] editSetCallbacks = [None] editGetCallbacks = [None] ###editWidgets = [self.conditionMenu] editWidgets = [None] self.conditionTable = ScrolledMatrix(master, initialRows=6, grid=(row, 0), gridSpan=(1,2), tipTexts=tipTexts, headingList=headingList, callback=self.selectCell, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks) # TBD: make directory editable row = row + 1 label = Label(master, text='Contour dir: ', grid=(row, 0), sticky='e') tipText = 'The directory location on disk into which contour files are saved' self.dir_label = Label(master, text='', grid=(row, 1), tipText=tipText) row = row + 1 label = Label(master, text='File name: ', grid=(row, 0), sticky='e') tipText = 'Sets the name of the contour file to save to disk' self.file_entry = Entry(master, grid=(row, 1), tipText=tipText) ##row = row + 1 ##label = Label(master, text='Contour levels: ') ##label.grid(row=row, column=0, sticky='e') ##self.levels_entry = FloatEntry(master, isArray=True, returnCallback=self.saveLevels) ##self.levels_entry.grid(row=row, column=1, sticky='ew') row = row + 1 tipTexts = ['Add a new contour region for including in or excluding from the contour file ', 'Remove the selected contour region from the table', 'Make the specified contour file using the input settings & regions'] texts = [ 'Add Condition', 'Delete Condition', 'Contour and Save' ] commands = [ self.addCondition, self.deleteCondition, self.contourAndSaveSpectrum ] self.buttons = UtilityButtonList(master, texts=texts, commands=commands, doClone=False, helpUrl=self.help_url, grid=(row, 0), gridSpan=(1,2), tipTexts=tipTexts) ### TBD disabled for now for n in range(2): self.buttons.buttons[n].config(state='disabled') self.curateNotifiers(self.registerNotify) self.updateSpectrum() self.updateDimMenu() def destroy(self): self.curateNotifiers(self.unregisterNotify) BasePopup.destroy(self) def curateNotifiers(self, notifyFunc): for clazz in ('Experiment', 'DataSource'): for func in ('__init__', 'delete', 'setName'): notifyFunc(self.updateNotifier, 'ccp.nmr.Nmr.%s' % clazz, func) def update(self, spectrum): if spectrum is self.spectrum: return self.spectrum = spectrum self.updateDimMenu() self.updateConditionTable() self.updateFile() ##self.updateLevels() def updateSpectrum(self, spectrum=None): if not spectrum: spectrum = self.spectrum spectra = self.parent.getSpectra() if spectra: if spectrum not in spectra: spectrum = spectra[0] index = spectra.index(spectrum) names = ['%s:%s' % (x.experiment.name, x.name) for x in spectra] else: index = 0 names = [] self.expt_spectrum.setup(names, spectra, index) self.update(spectrum) def updateNotifier(self, *extra): self.updateSpectrum() def updateFile(self, *extra): spectrum = self.spectrum if not spectrum: return analysisSpectrum = spectrum.analysisSpectrum if not analysisSpectrum: return dims = self.dim_menu.getText() dims = [int(dim) for dim in dims.split(', ')] (xdim, ydim) = dims storedContour = analysisSpectrum.findFirstStoredContour(dims=dims) if not storedContour: storedContour = analysisSpectrum.findFirstStoredContour() if storedContour: fileName = storedContour.path else: fileName = '%s_%s_%d_%d.cnt' % (spectrum.experiment.name, spectrum.name, xdim, ydim) path = self.getContourDir() self.dir_label.set(path) self.file_entry.set(fileName) def getContourDir(self): spectrum = self.spectrum if not spectrum: return '' analysisSpectrum = spectrum.analysisSpectrum if not analysisSpectrum: return '' url = analysisSpectrum.contourDir if url: # should be the case since set in Analysis.py path = url.dataLocation else: path = '' return path """ def saveFile(self, *extra): spectrum = self.spectrum if not spectrum: return analysisSpectrum = spectrum.analysisSpectrum if not analysisSpectrum: return fileName = self.file_entry.get() if fileName: application = self.project.application application.setValue(spectrum, keyword='contourFileName', value=fileName) """ def updateDimMenu(self): entries = [] spectrum = self.spectrum if spectrum: ndim = spectrum.numDim for xdim in range(1, ndim): dataDim = spectrum.findFirstDataDim(dim=xdim) if dataDim.className != 'SampledDataDim': for ydim in range(xdim+1, ndim+1): dataDim = spectrum.findFirstDataDim(dim=ydim) if dataDim.className != 'SampledDataDim': entries.append('%d, %d' % (xdim, ydim)) self.dim_menu.setup(entries, objects=None, index=0) def updateConditionTable(self, *extra): spectrum = self.spectrum if spectrum: ndim = spectrum.numDim else: ndim = 0 tipTexts = ['Whether to include or exclude the region in the contour file',] headingList = ['Condition'] ###editWidgets = [self.conditionMenu] + 2*ndim*[self.regionEntry] ###editGetCallbacks = [self.getCondition] ###editSetCallbacks = [self.setCondition] editWidgets = [None] + 2*ndim*[self.regionEntry] editGetCallbacks = [None] editSetCallbacks = [None] for i in range(ndim): dim = i + 1 headingList.extend(['Dim %d Min' % dim, 'Dim %d Max' % dim]) editGetCallbacks.append(lambda obj, i=i: self.getRegionMin(obj, i)) editGetCallbacks.append(lambda obj, i=i: self.getRegionMax(obj, i)) editSetCallbacks.append(lambda obj, i=i: self.setRegionMin(obj, i)) editSetCallbacks.append(lambda obj, i=i: self.setRegionMax(obj, i)) tipTexts.append('Lower bound of dimension %d region to include or exclude' % dim) tipTexts.append('Upper bound of dimension %d region to include or exclude' % dim) objectList = [] textMatrix = [] self.spectrumConditions = self.getSpectrumConditions() for spectrumCondition in self.spectrumConditions: (condition, region) = spectrumCondition objectList.append(spectrumCondition) textRow = [condition] for i in range(ndim): r = region[i] if r: rMin = r[0] rMax = r[1] else: rMin = rMax = None textRow.append(rMin) textRow.append(rMax) textMatrix.append(textRow) self.conditionTable.update(objectList=objectList, textMatrix=textMatrix, headingList=headingList, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets) """ def updateLevels(self): spectrum = self.getSpectrum() if spectrum: analysisSpectrum = spectrum.analysisSpectrum levels = analysisSpectrum.posLevels + analysisSpectrum.negLevels scale = spectrum.scale / self.analysisProject.globalContourScale levels = [ level/scale for level in levels ] else: levels = [] self.levels_entry.set(levels) def saveLevels(self): spectrum = self.spectrum if not spectrum: return """ def getRegionMin(self, spectrumCondition, dim): #print 'getRegionMin' (condition, region) = spectrumCondition self.regionEntry.set(region[dim][0]) def setRegionMin(self, spectrumCondition, dim): #print 'setRegionMin' (condition, region) = spectrumCondition (r0, r1) = region[dim] r = self.regionEntry.get() region[dim] = (r, r1) self.setSpectrumConditions() self.updateConditionTable() def getRegionMax(self, spectrumCondition, dim): #print 'getRegionMax' (condition, region) = spectrumCondition self.regionEntry.set(region[dim][1]) def setRegionMax(self, spectrumCondition, dim): #print 'setRegionMax' (condition, region) = spectrumCondition (r0, r1) = region[dim] r = self.regionEntry.get() region[dim] = (r0, r) self.setSpectrumConditions() self.updateConditionTable() def setRegion(self, *extra): spectrumCondition = self.getCurrentObject() if spectrumCondition and self.col is not None: col = self.col - 1 # -1 because of condition dim = col / 2 if col % 2: # max self.setRegionMax(spectrumCondition, dim) else: # min self.setRegionMin(spectrumCondition, dim) """ not needed for now def getCondition(self, spectrumCondition): #print 'getCondition' (condition, region) = spectrumCondition self.conditionMenu.set(condition) def setCondition(self, spectrumCondition): #print 'setCondition' spectrumCondition[0] = self.conditionMenu.get() self.setSpectrumConditions() def selectedCondition(self, ind, condition): spectrumCondition = self.getCurrentObject() if spectrumCondition is not None: self.setCondition(spectrumCondition) """ def getSpectrumConditions(self): spectrum = self.spectrum if not spectrum: return [] application = self.project.application spectrumConditions = application.getValue(spectrum, keyword='contourFileConditions') if spectrumConditions: spectrumConditions = eval(spectrumConditions) else: region = [] for i in range(spectrum.numDim): dim = i + 1 region.append(self.getWholeRegion(spectrum, dim)) spectrumCondition = ['include', region] spectrumConditions = [spectrumCondition] self.setSpectrumConditions(spectrumConditions) return spectrumConditions def setSpectrumConditions(self, spectrumConditions = None): spectrum = self.spectrum if spectrum: if not spectrumConditions: spectrumConditions = self.spectrumConditions application = self.project.application application.setValue(spectrum, keyword='contourFileConditions', value=str(spectrumConditions)) def getDimMin(self, spectrum, dim): dataDim = spectrum.findFirstDataDim(dim=dim) if dataDim.className == 'SampledDataDim': r = 1.0 else: converter = UnitConverter.pnt2ppm dataDimRef = ExperimentBasic.getPrimaryDataDimRef(dataDim) r = converter(float(dataDim.numPoints), dataDimRef) return r def getDimMax(self, spectrum, dim): dataDim = spectrum.findFirstDataDim(dim=dim) if dataDim.className == 'SampledDataDim': r = float(dataDim.numPoints) else: converter = UnitConverter.pnt2ppm dataDimRef = ExperimentBasic.getPrimaryDataDimRef(dataDim) r = converter(1.0, dataDimRef) return r def getWholeRegion(self, spectrum, dim): rMin = self.getDimMin(spectrum, dim) rMax = self.getDimMax(spectrum, dim) return (rMin, rMax) def getCurrentObject(self): # sometimes row highlighting stops so obj passed into selectCell might no longer be current # so instead use direct interrogation of scrolledMatrix obj = self.conditionTable.currentObject return obj def selectCell(self, obj, row, col): # see note about obj in getCurrentObject() self.col = col def addCondition(self): spectrum = self.spectrum if spectrum: ndim = spectrum.numDim spectrumCondition = ['exclude', ndim*[(None,None)]] self.spectrumConditions.append(spectrumCondition) self.setSpectrumConditions() self.updateConditionTable() def deleteCondition(self): spectrumCondition = self.getCurrentObject() if spectrumCondition: self.spectrumConditions.remove(spectrumCondition) self.setSpectrumConditions() self.updateConditionTable() def continueIfFileNameUsed(self, fileName): result = True storedContoursToDelete = [] for analysisSpectrum in self.analysisProject.analysisSpectra: for storedContour in analysisSpectrum.storedContours: if storedContour.fullPath == fileName: storedContoursToDelete.append(storedContour) if storedContoursToDelete: if len(storedContoursToDelete) == 1: s = '' t = 's' else: s = 's' t = '' result = showYesNo('File used', 'Stored contour%s already use%s this fileName, and will be deleted: continue?' % (s, t), parent=self) if result: for storedContour in storedContoursToDelete: try: storedContour.delete() except: pass return result def contourAndSaveSpectrum(self): spectrum = self.spectrum if not spectrum: return if not self.spectrumConditions: return ###self.saveFile() fileName = self.file_entry.get() if not fileName: showError('No filename', 'No filename given', parent=self) return contourDir = self.getContourDir() fullPath = joinPath(contourDir, fileName) if not self.continueIfFileNameUsed(fullPath): return directory = os.path.dirname(fullPath) if not os.path.exists(directory): os.makedirs(directory) dims = self.dim_menu.getText() (xdim, ydim) = [int(dim) for dim in dims.split(', ')] ##self.saveLevels() ##levels = self.levels_entry.get() ##if not levels: ## showError('No levels', 'No contour levels given', parent=self) ## return analysisSpectrum = spectrum.analysisSpectrum levels = analysisSpectrum.posLevels + analysisSpectrum.negLevels scale = spectrum.scale / self.analysisProject.globalContourScale levels = [ level/scale for level in levels ] spectrumCondition = self.spectrumConditions[0] (condition, region) = spectrumCondition ndim = spectrum.numDim firstInt = ndim * [0] lastInt = ndim * [0] for i in range(ndim): try: (firstInt[i], lastInt[i]) = self.convertToPoints(spectrum, i, region[i]) except Exception, e: showError('Invalid region', str(e), parent=self) try: #print 'about to saveSpectrumContours', fullPath, xdim, ydim, levels, firstInt, lastInt saveSpectrumContours(spectrum, fullPath, xdim, ydim, levels, firstInt, lastInt, mem_cache=self.parent.mem_cache) except Exception, e: showError('Save error', str(e), parent=self) return
class EditExperimentSeriesPopup(BasePopup): """ **Setup Experiment Series for Chemical Shift and Intensity Changes** The purpose of this popup is to setup ordered groups of experiments that are related by the variation in some condition or parameter, but otherwise the kind of experiment being run is the same. For example the user could setup experiments for a temperature series, ligand binding titration or relaxation rate measurement. The layout is divided into two tables. The upper table shows all of the series that are known to the current project and the lower table shows all of the experiments or planes that comprise the selected series. These series relate to both groups of separate experiments (and hence spectra) and also single experiment where one dimension is not for NMR frequency but rather a "sampled" dimension and thus effectively combines many experiments (e.g. for different T1 values) as different planes. A stack of effectively 2D experiments combined in this manner is typically referred to as pseudo-3D. - The experiment is 3D but there are only two NMR dimensions. Series which are stacks of planes in a single experiment entity are automatically detected and entered into the table once they are loaded. Series that are constructed from multiple, separate experiments however must be setup by the user. To setup a new series of this kind [Add Series] makes a new, empty NMR series. Next the user should change the "Parameter varied" column to specify what type of thing is varied between the different experiments. The user then adds experiments into this series with the [Add Series Point] function at the bottom. Once the points have been added to the series the name of the experiment for each point may be changed. Initially, arbitrary experiments appear for the series points, so these almost always have to be adjusted. Once a stacked-plane experiment or series of experiments is setup, the user next sets (or checks) the value of the parameter associated with each point in the lower table. When loading stacked-plane experiments these values may come though automatically, if they are present in the spectrum header or parameter file. Given a completed NMR series specification the user may then extract the relevant data from the series using one of the analysis tools like `Follow Intensity Changes`_ or `Follow Shift Changes`_. **Caveats & Tips** Make sure the "Parameter Varied" for a given NMR series is appropriate to the type of analysis being performed. Many tools that extract T1 or Kd measurements for example look for specific types of series. The "Set/Unset Ref Plane" function is only used in certain kinds of series such as those that use trains of CPMG pulses. .. _`Follow Intensity Changes`: CalcRatesPopup.html .. _`Follow Shift Changes`: FollowShiftChangesPopup.html """ def __init__(self, parent, *args, **kw): self.guiParent = parent self.expSeries = None self.conditionPoint = None self.waiting = 0 BasePopup.__init__(self, parent, title="Experiment : NMR Series", **kw) def body(self, guiFrame): self.geometry("500x500") self.nameEntry = Entry(self, text='', returnCallback=self.setName, width=12) self.detailsEntry = Entry(self, text='', returnCallback=self.setDetails, width=16) self.valueEntry = FloatEntry(self, text='', returnCallback=self.setValue, width=10) self.errorEntry = FloatEntry(self, text='', returnCallback=self.setError, width=8) self.conditionNamesPulldown = PulldownList(self, callback=self.setConditionName, texts=self.getConditionNames()) self.unitPulldown = PulldownList(self, callback=self.setUnit, texts=self.getUnits()) self.experimentPulldown = PulldownList(self, callback=self.setExperiment) guiFrame.grid_columnconfigure(0, weight=1) row = 0 frame = Frame(guiFrame, grid=(row, 0)) frame.expandGrid(None,0) div = LabelDivider(frame, text='Current Series', grid=(0, 0)) utilButtons = UtilityButtonList(frame, helpUrl=self.help_url, grid=(0,1)) row += 1 frame0 = Frame(guiFrame, grid=(row, 0)) frame0.expandGrid(0,0) tipTexts = ['The serial number of the experiment series, but left blank if the series as actually a pseudo-nD experiment (with a sampled non-frequency axis)', 'The name of the experiment series, which may be a single pseudo-nD experiment', 'The number of separate experiments (and hence spectra) present in the series', 'The kind of quantity that varies for different experiments/planes within the NMR series, e.g. delay time, temperature, ligand concentration etc.', 'The number of separate points, each with a separate experiment/plane and parameter value, in the series'] headingList = ['#','Name','Experiments','Parameter\nVaried','Num\nPoints'] editWidgets = [None, self.nameEntry, None, self.conditionNamesPulldown, None] editGetCallbacks = [None, self.getName, None, self.getConditionName, None] editSetCallbacks = [None, self.setName, None, self.setConditionName, None] self.seriesMatrix = ScrolledMatrix(frame0, tipTexts=tipTexts, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, headingList=headingList, callback=self.selectExpSeries, deleteFunc=self.deleteExpSeries, grid=(0,0), gridSpan=(None, 3)) tipTexts = ['Make a new, blank NMR series specification in the CCPN project', 'Delete the selected NMR series from the project, although any component experiments remain. Note you cannot delete pseudo-nD series; delete the actual experiment instead', 'Colour the spectrum contours for each experiment in the selected series (not pseudo-nD) using a specified scheme'] texts = ['Add Series','Delete Series', 'Auto Colour Spectra'] commands = [self.addExpSeries,self.deleteExpSeries, self.autoColorSpectra] self.seriesButtons = ButtonList(frame0, texts=texts, commands=commands, grid=(1,0), tipTexts=tipTexts) label = Label(frame0, text='Scheme:', grid=(1,1)) tipText = 'Selects which colour scheme to apply to the contours of (separate) experiments within an NMR series' self.colorSchemePulldown = PulldownList(frame0, grid=(1,2), tipText=tipText) row += 1 div = LabelDivider(guiFrame, text='Experimental Parameters & Conditions', grid=(row, 0)) row += 1 guiFrame.grid_rowconfigure(row, weight=1) frame1 = Frame(guiFrame, grid=(row, 0)) frame1.expandGrid(0,0) tipTexts = ['The kind of experimental parameter that is being used to define the NMR series', 'The experiment that corresponds to the specified parameter value; can be edited from an arbitrary initial experiment', 'The numeric value of the parameter (condition) that relates to the experiment or point in the NMR series', 'The estimated error in value of the condition', 'The measurement unit in which the value of the condition is represented'] headingList = ['Parameter','Experiment','Value','Error','Unit'] editWidgets = [None,self.experimentPulldown,self.valueEntry,self.errorEntry, self.unitPulldown] editGetCallbacks = [None,self.getExperiment, self.getValue, self.getError, self.getUnit] editSetCallbacks = [None,self.setExperiment, self.setValue, self.setError, self.setUnit] self.conditionPointsMatrix = ScrolledMatrix(frame1, grid=(0,0), tipTexts=tipTexts, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, headingList=headingList, callback=self.selectConditionPoint, deleteFunc=self.deleteConditionPoint) self.conditionPointsMatrix.doEditMarkExtraRules = self.conditionTableShow tipTexts = ['Add a new point to the NMR series with an associated parameter value and experiment', 'Remove the selected point from the series, including any associated parameter value', 'For appropriate kinds of NMR series, set or unset a point as representing the plane to use as a reference'] texts = ['Add Series Point','Delete Series Point','Set/Unset Ref Plane'] commands = [self.addConditionPoint,self.deleteConditionPoint,self.setSampledReferencePlane] self.conditionPointsButtons = ButtonList(frame1, texts=texts, commands=commands, tipTexts=tipTexts, grid=(1,0)) self.updateAfter() self.updateColorSchemes() self.administerNotifiers(self.registerNotify) def administerNotifiers(self, notifyFunc): #for func in ('__init__', 'delete','setName'): for func in ('__init__', 'delete','setName','setConditionNames', 'addConditionName','removeConditionName'): notifyFunc(self.updateAfter,'ccp.nmr.Nmr.NmrExpSeries', func) for func in ('__init__', 'delete','setName'): notifyFunc(self.updateExperiments,'ccp.nmr.Nmr.Experiment', func) for func in ('__init__', 'delete'): notifyFunc(self.updateDataDim,'ccp.nmr.Nmr.SampledDataDim', func) for func in ('__init__', 'delete','setCondition','setUnit','setValue','setError'): notifyFunc(self.updateConditionsAfter,'ccp.nmr.Nmr.SampleCondition', func) for func in ('__init__', 'delete','setCondition'): notifyFunc(self.updateAfter,'ccp.nmr.Nmr.SampleCondition', func) for func in ('setConditionVaried', 'setPointErrors', 'addPointError', 'removePointError', 'setPointValues','addPointValue', 'removePointValue','setUnit'): notifyFunc(self.updateAfter,'ccp.nmr.Nmr.SampledDataDim', func) for func in ('__init__', 'delete'): notifyFunc(self.updateColorSchemes,'ccpnmr.AnalysisProfile.ColorScheme', func) def open(self): self.updateAfter() BasePopup.open(self) def updateColorSchemes(self, scheme=None): index = 0 prevScheme = self.colorSchemePulldown.getObject() schemes = getHueSortedColorSchemes(self.analysisProfile) schemes = [s for s in schemes if len(s.colors) > 1] colors = [list(s.colors) for s in schemes] if schemes: names = [s.name for s in schemes] if prevScheme in schemes: index = schemes.index(prevScheme) else: names = [] self.colorSchemePulldown.setup(names, schemes, index, colors) def autoColorSpectra(self): if self.expSeries and (self.expSeries.className != 'Experiment'): scheme = self.colorSchemePulldown.getObject() if scheme: colors = scheme.colors else: colors = ['#FF0000','#00FF00','#0000FF'] cdict = getNmrExpSeriesSampleConditions(self.expSeries) conditionName = list(self.expSeries.conditionNames)[0] expList = [] for sampleCondition in cdict.get(conditionName, []): expList.append( (sampleCondition.value, sampleCondition.parent.experiments) ) expList.sort() m = len(expList)-1.0 c = len(colors)-1 for i, (value, experiments) in enumerate(expList): p = c*i/m j = int(p) r1, g1, b1 = Color.hexToRgb(colors[j]) r2, g2, b2 = Color.hexToRgb(colors[min(c,j+1)]) f2 = p-j f1 = 1.0-f2 r = (r1*f1)+(r2*f2) g = (g1*f1)+(g2*f2) b = (b1*f1)+(b2*f2) hexColor = Color.hexRepr(r,g,b) for experiment in experiments: for spectrum in experiment.dataSources: if spectrum.dataType == 'processed': analysisSpec = getAnalysisSpectrum(spectrum) if analysisSpec.posColors: analysisSpec.posColors = [hexColor,] elif analysisSpec.negColors: analysisSpec.negColors = [hexColor,] def getUnusedExperiments(self): sampleExperiments = getSampledDimExperiments(self.nmrProject) experiments = [] for experiment in self.nmrProject.sortedExperiments(): if experiment in sampleExperiments: continue if self.expSeries and (self.expSeries.className != 'Experiment'): if experiment in self.expSeries.experiments: continue experiments.append(experiment) return experiments def conditionTableShow(self, object, row, col): if type(object) is type(()): dataDim, index = object refPlane = dataDim.analysisDataDim.refSamplePlane if refPlane == index: return False if col == 1: return False return True def setSampledReferencePlane(self): if self.expSeries and (self.expSeries.className == 'Experiment'): if self.conditionPoint: dataDim, point = self.conditionPoint analysisDataDim = dataDim.analysisDataDim refPoint = analysisDataDim.refSamplePlane if refPoint == point: analysisDataDim.refSamplePlane = None else: analysisDataDim.refSamplePlane = point self.updateAfter() def checkAddSampleCondition(self, experiment): conditionSet = getExperimentConditionSet(experiment) conditionName = self.expSeries.conditionNames[0] condDict = getNmrExpSeriesSampleConditions(self.expSeries) sampleConditions = condDict.get(conditionName, []) units = [sc.unit for sc in sampleConditions if sc.unit] if units: sortList = list(set(units)) sortList.sort(key = lambda u:units.count(u)) unit = sortList[-1] else: unit = CONDITION_UNITS_DICT[conditionName][0] condition = conditionSet.findFirstSampleCondition(condition=conditionName) if not condition: condition = conditionSet.newSampleCondition(condition=conditionName, unit=unit, value=0.0, error=0.0) def addConditionPoint(self): if self.expSeries and (self.expSeries.className != 'Experiment'): experiments = self.getUnusedExperiments() if not experiments: showWarning('Warning','No experiments available', parent=self) return experiment = experiments[0] if experiment not in self.expSeries.experiments: self.expSeries.addExperiment(experiment) self.checkAddSampleCondition(experiment) self.updateAfter() def deleteConditionPoint(self, *event): if self.conditionPoint and (self.expSeries.className != 'Experiment'): if showOkCancel('Confirm','Really delete series point?', parent=self): conditionSet = self.conditionPoint.sampleConditionSet experiments = [e for e in conditionSet.experiments if e in self.expSeries.experiments] for experiment in experiments: self.expSeries.removeExperiment(experiment) for experiment in experiments: for expSeries in experiment.nmrExpSeries: if self.conditionPoint.condition in self.expSeries.conditionNames: break else: continue break else: self.conditionPoint.delete() self.conditionPoint = None def selectConditionPoint(self, object, row, col): if object: self.conditionPoint = object self.updateButtons() def selectExpSeries(self, object, row, col): if object: self.expSeries = object self.checkExperimentConditionsConsistent() self.updateConditions() def checkExperimentConditionsConsistent(self): if self.expSeries.className != 'Experiment': for experiment in self.expSeries.experiments: self.checkAddSampleCondition(experiment) def getUnits(self): units = [] if self.expSeries: if self.expSeries.className == 'Experiment': conditionName = getExperimentSampledDim(self.expSeries).conditionVaried else: conditionName = self.expSeries.conditionNames[0] units = CONDITION_UNITS_DICT.get(conditionName) if not units: units = ['?',] return units def getUnit(self, sampleCondition): index = -1 units = self.getUnits() if units: if sampleCondition: if type(sampleCondition) is type(()): dataDim, index = sampleCondition unit = dataDim.unit else: unit = sampleCondition.unit if unit not in units: unit = units[0] index = units.index(unit) self.unitPulldown.setup(units,units,index) def setUnit(self, obj): name = self.unitPulldown.getObject() if self.conditionPoint: if type(self.conditionPoint) is type(()): dataDim, index = self.conditionPoint dataDim.setUnit(name) else: self.conditionPoint.setUnit(name) def getConditionNames(self): if self.expSeries and (self.expSeries.className == 'Experiment'): names = ['delay time','mixing time','num delays','pulsing frequency','gradient strength'] else: names = CONDITION_UNITS_DICT.keys() names.sort() return names def setConditionName(self, obj): name = self.conditionNamesPulldown.getObject() if self.expSeries: if self.expSeries.className == 'Experiment': dataDim = getExperimentSampledDim(self.expSeries) dataDim.conditionVaried = name else: self.expSeries.setConditionNames([name,]) def getConditionName(self, expSeries): index = 0 names = self.getConditionNames() if names: if expSeries: if expSeries.className == 'Experiment': name = getExperimentSampledDim(expSeries).conditionVaried else: name = expSeries.conditionNames[0] if name: index = names.index(name) else: index = 0 self.conditionNamesPulldown.setup(names,names,index) def getName(self, expSeries): if expSeries : self.nameEntry.set(expSeries.name) def setName(self, event): text = self.nameEntry.get() if text and text != ' ': self.expSeries.setName( text ) def getValue(self, conditionPoint): if conditionPoint: if type(self.conditionPoint) is type(()): dataDim, index = conditionPoint value = dataDim.pointValues[index] else: value = conditionPoint.value self.valueEntry.set(value) def setValue(self, event): value = self.valueEntry.get() if value is not None: if type(self.conditionPoint) is type(()): dataDim, index = self.conditionPoint values = list(dataDim.pointValues) values[index] = value dataDim.setPointValues(values) else: self.conditionPoint.setValue( value ) def getError(self, conditionPoint): if conditionPoint: if type(self.conditionPoint) is type(()): dataDim, index = conditionPoint if index < len(dataDim.pointErrors): error = dataDim.pointValues[index] else: error = 0.0 else: error = conditionPoint.error self.errorEntry.set(error) def setError(self, event): value = self.errorEntry.get() if value is not None: if type(self.conditionPoint) is type(()): dataDim, index = self.conditionPoint pointErrors = dataDim.pointErrors if pointErrors: values = list(pointErrors) else: values = [0.0] * dataDim.numPoints values[index] = value dataDim.setPointErrors(values) else: self.conditionPoint.setError( value ) def getDetails(self, expSeries): if expSeries : self.detailsEntry.set(expSeries.details) def setDetails(self, event): text = self.detailsEntry.get() if text and text != ' ': self.expSeries.setDetails( text ) def addExpSeries(self): expSeries = self.nmrProject.newNmrExpSeries(conditionNames=['delay time',]) def deleteExpSeries(self, *event): if self.expSeries and (self.expSeries.className != 'Experiment'): if showOkCancel('Confirm','Really delete series?', parent=self): self.expSeries.delete() self.expSeries = None self.conditionPoint = None def getExperiments(self): if self.expSeries and (self.expSeries.className == 'Experiment'): return [self.expSeries,] else: return self.nmrProject.sortedExperiments() def getExperiment(self, sampleCondition): index = 0 names = [] if self.conditionPoint and (type(self.conditionPoint) != type(())): index = 0 experiment = self.conditionPoint.parent.findFirstExperiment() experiments = self.getUnusedExperiments() name = experiment.name names = [name] + [e.name for e in experiments] experiments = [experiment,] + experiments self.experimentPulldown.setup(names,experiments,index) def setExperiment(self, obj): experiment = self.experimentPulldown.getObject() if self.conditionPoint and (type(self.conditionPoint) != type(())): conditionSet = getExperimentConditionSet(experiment) if conditionSet is not self.conditionPoint.parent: if experiment not in self.expSeries.experiments: self.expSeries.addExperiment(experiment) unit = self.conditionPoint.unit if not unit: unit = CONDITION_UNITS_DICT[self.expSeries.conditionNames[0]] condition = self.conditionPoint.condition experiments = set(self.expSeries.experiments) for experiment0 in self.conditionPoint.parent.experiments: if experiment0 in experiments: experiments.remove(experiment0) self.expSeries.experiments = experiments value = self.conditionPoint.value error = self.conditionPoint.error self.conditionPoint.delete() self.conditionPoint = conditionSet.findFirstSampleCondition(condition=condition) if self.conditionPoint: self.conditionPoint.unit = unit self.updateAfter() else: self.conditionPoint = conditionSet.newSampleCondition(condition=condition,unit=unit, value=value,error=error) def updateDataDim(self, sampledDataDim): experiment = sampledDataDim.dataSource.experiment self.updateExperiments(experiment) def updateExperiments(self, experiment): experiments = self.getExperiments() names = [e.name for e in experiments] self.experimentPulldown.setup(names, experiments,0) if getExperimentSampledDim(experiment): self.updateAfter() elif self.expSeries: if self.expSeries.className == 'Experiment': if experiment is self.expSeries: self.updateConditionsAfter() elif experiment in self.expSeries.experiments: self.updateConditionsAfter() def updateConditionsAfter(self, sampleCondition=None): if self.waitingConditions: return if sampleCondition: experiments = sampleCondition.sampleConditionSet.experiments for experiment in experiments: if self.expSeries.className == 'Experiment': if experiment is self.expSeries: self.waitingConditions = True self.after_idle(self.updateConditions) break elif experiment in self.expSeries.experiments: self.waitingConditions = True self.after_idle(self.updateConditions) break else: self.waitingConditions = True self.after_idle(self.updateConditions) def updateConditions(self): self.updateButtons() objectList = [] textMatrix = [] colorMatrix = [] nCols = len(self.conditionPointsMatrix.headingList) defaultColors = [None] * nCols if self.expSeries: if self.expSeries.className == 'Experiment': dataDim = getExperimentSampledDim(self.expSeries) analysisDataDim = getAnalysisDataDim(dataDim) conditionVaried = dataDim.conditionVaried expName = self.expSeries.name unit = dataDim.unit pointValues = dataDim.pointValues pointErrors = dataDim.pointErrors refPlane = analysisDataDim.refSamplePlane for i in range(dataDim.numPoints): if i < len(pointErrors): error = pointErrors[i] else: error = None pointText = ':%3d' % (i+1) if i == refPlane: datum = ['* Ref Plane *', expName+pointText, None, None, None] colorMatrix.append(['#f08080'] * nCols) else: datum = [conditionVaried , expName+pointText, pointValues[i], error, unit] colorMatrix.append(defaultColors) textMatrix.append(datum) objectList.append((dataDim, i)) else: condDict = getNmrExpSeriesSampleConditions(self.expSeries) conditionNames = self.expSeries.conditionNames for conditionName in conditionNames: for sampleCondition in condDict.get(conditionName, []): datum = [sampleCondition.condition, ' '.join([e.name for e in sampleCondition.parent.experiments]), sampleCondition.value, sampleCondition.error, sampleCondition.unit] textMatrix.append(datum) objectList.append(sampleCondition) colorMatrix.append(defaultColors) self.conditionPointsMatrix.update(objectList=objectList, colorMatrix=colorMatrix, textMatrix=textMatrix) self.waitingConditions = 0 def updateAfter(self, object=None): if self.waiting: return else: self.waiting = True self.after_idle(self.update) def updateButtons(self): if self.expSeries is None: self.seriesButtons.buttons[1].disable() self.seriesButtons.buttons[2].disable() self.conditionPointsButtons.buttons[0].disable() self.conditionPointsButtons.buttons[1].disable() self.conditionPointsButtons.buttons[2].disable() elif self.expSeries.className == 'Experiment': self.seriesButtons.buttons[1].disable() self.seriesButtons.buttons[2].disable() self.conditionPointsButtons.buttons[0].disable() self.conditionPointsButtons.buttons[1].disable() self.conditionPointsButtons.buttons[2].enable() else: self.seriesButtons.buttons[1].enable() self.seriesButtons.buttons[2].enable() self.conditionPointsButtons.buttons[0].enable() self.conditionPointsButtons.buttons[2].disable() if self.conditionPoint is None: self.conditionPointsButtons.buttons[1].disable() else: self.conditionPointsButtons.buttons[1].enable() def update(self): self.updateButtons() objectList = [] textMatrix = [] for experiment in getSampledDimExperiments(self.nmrProject): getExperimentConditionSet(experiment) sampledDim = getExperimentSampledDim(experiment) datum = [None, experiment.name, 1, sampledDim.conditionVaried, sampledDim.numPoints] textMatrix.append(datum) objectList.append(experiment) for expSeries in self.nmrProject.sortedNmrExpSeries(): experiments = expSeries.experiments conditionSets = len([e.sampleConditionSet for e in experiments if e.sampleConditionSet]) datum = [expSeries.serial, expSeries.name or ' ', len(experiments), ','.join(expSeries.conditionNames), conditionSets] textMatrix.append(datum) objectList.append(expSeries) self.seriesMatrix.update(objectList=objectList, textMatrix=textMatrix) self.updateConditions() self.waiting = False def destroy(self): self.administerNotifiers(self.unregisterNotify) BasePopup.destroy(self)
class CreateAxisTypePopup(BasePopup): def __init__(self, parent, *args, **kw): self.measurementType = None BasePopup.__init__(self, parent=parent, title='Create axis type', modal=True, **kw) def body(self, master): master.grid_columnconfigure(1, weight=1) row = 0 label = Label(master, text='Axis name: ', grid=(row, 0)) tipText = 'Short text name for new type of axis e.g. "17O"' self.name_entry = Entry(master, width=15, grid=(row, 1), tipText=tipText) row += 1 label = Label(master, text='Axis region: ', grid=(row, 0)) tipText = 'Comma separated values for the upper and lower bound of the axis allowed range of values' self.region_entry = FloatEntry(master, text=[0.0, 1.0], isArray=True, width=15, grid=(row, 1), tipText=tipText) row += 1 label = Label(master, text='Measurement type:', grid=(row, 0)) tipText = 'The physical entity that is being measured along the axis' self.measurement_list = PulldownList(master, tipText=tipText) self.measurement_list.grid(row=row, column=1, sticky='w') row += 1 label = Label(master, text='Dimension is sampled: ', grid=(row, 0)) tipText = 'Whether the axis is discretely sampled or a continuous range (albeit on a grid)' self.sampled_popup = BooleanPulldownMenu(master, grid=(row, 1), tipText=tipText) row += 1 label = Label(master, text='Decimal places: ', grid=(row, 0)) tipText = 'The number of decimal places that the axis values are rounded to for display purposes' self.decimals_entry = IntEntry(master, text=0, width=15, grid=(row, 1), tipText=tipText) row += 1 label = Label(master, text='Peak size: ', grid=(row, 0)) tipText = 'The relative scale for the peak symbol (i.e the "X" shape) size compared to other axes' self.peak_size_entry = FloatEntry(master, text=1.0, width=15, grid=(row, 1), tipText=tipText) row += 1 label = Label(master, text='Allowed axis units:', grid=(row, 0)) tipTexts = [ 'Units of measurement allowed for this kind of axis', ] units = [au.unit for au in self.parent.getAxisUnits()] selected = [True] * len(units) self.units_list = CheckButtons(master, units, selected=selected, direction='vertical', grid=(row, 1), tipTexts=tipTexts) row += 1 tipTexts = [ 'Make a new axis specification of the selected type and close this popup' ] texts = ['Create'] commands = [self.ok] buttons = UtilityButtonList(master, texts=texts, commands=commands, doClone=False, closeText='Cancel', helpUrl=self.help_url, grid=(row, 0), gridSpan=(1, 2), tipTexts=tipTexts) master.grid_rowconfigure(row, weight=1) self.update() def update(self, *extra): measurementType = self.measurementType measurementTypes = self.parent.getMeasurementTypes() if measurementTypes: if measurementType not in measurementTypes: self.measurementType = measurementType = measurementTypes[0] index = measurementTypes.index(measurementType) else: index = 0 self.measurementType = None self.measurement_list.setup(measurementTypes, None, index) def apply(self): name = self.name_entry.get() if (not name): showError('No name', 'Need to enter name', self) return False names = [axisType.name for axisType in self.analysisProject.axisTypes] if (name in names): showError('Repeated name', 'Name already used', self) return False region = self.region_entry.get() if ((region is None) or (len(region) != 2)): showError('Region error', 'Region must be float array of length two', self) return False if (region[0] >= region[1]): showError('Region error', 'Region must have first number < second number', self) return False measurementType = self.measurement_list.getText() isSampled = self.sampled_popup.getSelected() numDecimals = self.decimals_entry.get() if ((numDecimals is None) or (numDecimals < 0)): showError('Decimals error', 'Number of decimal places must be >= 0', self) return False peakSize = self.peak_size_entry.get() if ((peakSize is None) or (peakSize <= 0)): showError('Peak size error', 'Peak size must be > 0', self) return False selected = self.units_list.getSelected() allUnits = self.parent.getAxisUnits() axisUnits = [au for au in allUnits if au.unit in selected] self.analysisProject.newAxisType(name=name, region=region, isSampled=isSampled, axisUnits=axisUnits, numDecimals=numDecimals, peakSize=peakSize, measurementType=measurementType) return True