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 EditFitGraphPopup(BasePopup): """ **Analyse Function Curve Fitting to Peak Series Data** This popup is used to display the fit of a curve, of the displayed equation type, to data that has been extracted for a group of spectrum peaks from an NMR series. Precisely which kind of data is being fitted depends on the parent tool that this popup window was launched from. For example, for the `Follow Intensity Changes`_ tool the displayed graph is of a time or frequency value on the "X" axis (e.g. T1) verses peak intensity. For the `Follow Shift Changes`_ system the plot is for the parameters of the experiment titration on the "X" axis (e.g concentration) verses chemical shift distance. The upper graph shows the peak data that was used; the blue points, and the points of the fitted theoretical curve; red points. The lower table lists the data points for all of the peaks in the group, to which the data relates. There will be one peak for each row of the table, and hence point on the "X" axis. For each data point in the table the corresponding peak from which the data was extracted may be located with the "Follow in window" option, using the stated spectrum window and making marker lines as desired. Alternatively, the peak may be viewed in a table with [Show Peak] In general operation this system is used to check how well the selected function curve fits the peak data. The data that is displayed comes from the analysis tool that launched this popup. Any outliers may be removed using [Remove Point] (only with good reason) or the peaks themselves may be corrected if something has gone awry. Given that the display is launched from the selection of a specific group of peaks from a popup like `Follow Shift Changes`_ or `Follow Intensity Changes`_ and there are usually several peak groups that need to be analysed, the [Previous Set] and [Next Set] buttons can be used to quickly jump to the next peak group and see the next curve in the analysis results. .. _`Follow Intensity Changes`: CalcRatesPopup.html .. _`Follow Shift Changes`: FollowShiftChangesPopup.html """ def __init__(self, parent, dataFitting, getXYfunction, updateFunction, xLabel, yLabel, showObjectFunction=None, nextSetFunction=None, prevSetFunction=None, graphTitle='', **kw): self.guiParent = parent self.getXYfunction = getXYfunction self.updateFunction = updateFunction self.nextSetFunction = nextSetFunction self.prevSetFunction = prevSetFunction self.dataFitting = dataFitting self.object = None self.xLabel = xLabel self.yLabel = yLabel self.x = [] self.y = [] self.yFit = [] self.params = () self.chiSq = 0 self.method = dataFitting.fitFunction self.waiting = False self.noiseLevel = dataFitting.noiseLevel self.graphTitle = graphTitle self.windowPane = None self.mark = None BasePopup.__init__(self, parent=parent, title='Fit Graph', **kw) parent.protocol("WM_DELETE_WINDOW", self.close) def body(self, guiFrame): guiFrame.grid_columnconfigure(0, weight=1) row = 0 self.scrolledGraph = ScrolledGraph(guiFrame, width=400, height=300, symbolSize=5, symbols=['square', 'circle'], dataColors=['#000080', '#800000'], lineWidths=[0, 1], grid=(row, 0)) #self.scrolledGraph.setZoom(0.7) row += 1 frame = Frame(guiFrame, grid=(row, 0), sticky='ew') label = Label(frame, text='Fitting Function:', grid=(0, 0)) tipText = 'Selects which form of function to fit to the experimental data' self.methodPulldown = PulldownList(frame, self.changeMethod, grid=(0, 1), tipText=tipText) row += 1 frame = Frame(guiFrame, grid=(row, 0), sticky='ew') tipText = 'The effective equation of the final fitted graph, incorporating all parameters' self.equationLabel = Label(frame, text='Equation:', grid=(0, 0), tipText=tipText) tipText = 'The error in the fit of the selected parameterised function to the experimental data' self.errorLabel = Label(frame, text='Fit Error:', grid=(0, 1), tipText=tipText) row += 1 frame = Frame(guiFrame, grid=(row, 0), sticky='ew') label = Label(frame, text='Include x origin?:', grid=(0, 0)) tipText = 'Whether to include the x=0 point in the drawing' self.xOriginSelect = CheckButton(frame, callback=self.draw, grid=(0, 1), selected=False, tipText=tipText) label = Label(frame, text='Include y origin?:', grid=(0, 2)) tipText = 'Whether to include the y=0 point in the drawing' self.yOriginSelect = CheckButton(frame, callback=self.draw, grid=(0, 3), selected=False, tipText=tipText) label = Label(frame, text='Include y error?:', grid=(0, 4)) tipText = 'Whether to include the y error bars in the drawing (if these exist)' self.yErrorSelect = CheckButton(frame, callback=self.draw, grid=(0, 5), selected=False, tipText=tipText) row += 1 frame = Frame(guiFrame, grid=(row, 0), sticky='ew') label = Label(frame, text='Navigation Window:', grid=(0, 0)) tipText = 'Selects which spectrum window will be used for navigating to peak positions' self.windowPanePulldown = PulldownList(frame, self.changeWindow, grid=(0, 1), tipText=tipText) label = Label(frame, text='Follow in window?:', grid=(0, 2)) tipText = 'Whether to navigate to the position of the reference peak (for the group), in the selected window' self.followSelect = CheckButton(frame, callback=self.windowPaneNavigate, grid=(0, 3), selected=False, tipText=tipText) label = Label(frame, text='Mark Ref Peak?:', grid=(0, 4)) tipText = 'Whether to put a multi-dimensional cross-mark through the reference peak position, so it can be identified in spectra' self.markSelect = CheckButton(frame, callback=None, tipText=tipText, grid=(0, 5), selected=False) row += 1 guiFrame.grid_rowconfigure(row, weight=1) tipTexts = [ 'The number of the data point, in order of increasing X-axis value', 'For each point, the value of the parameter which is varied in the NMR series, e.g. T1, temperature, concentration etc.', 'For each point, the experimental value being fitted, e.g. peak intensity of chemical shift distance', 'The value of the best-fit function at the X-axis location', 'The difference between the experimental (Y-axis) value and the fitted value', 'The error in the experimental (Y-axis) value' ] headingList = ['Point', 'x', 'y', 'Fitted y', u'\u0394', 'y error'] self.scrolledMatrix = ScrolledMatrix(guiFrame, headingList=headingList, callback=self.selectObject, tipTexts=tipTexts, grid=(row, 0)) row += 1 tipTexts = [ 'Remove the selected data point, optionally removing the underlying peak', 'Show a table of spectrum peaks that correspond to the selected data point' ] texts = ['Remove Point', 'Show Peak'] commands = [self.removePoint, self.showObject] if self.prevSetFunction: texts.append('Previous Set') tipTexts.append( 'Move to the previous set of fitted values; the next group of peaks, often corresponding to a different residue or resonance' ) commands.append(self.prevSet) if self.nextSetFunction: tipTexts.append( 'Move to the next set of fitted values; the next group of peaks, often corresponding to a different residue or resonance' ) texts.append('Next Set') commands.append(self.nextSet) bottomButtons = UtilityButtonList(guiFrame, texts=texts, commands=commands, helpUrl=self.help_url, tipTexts=tipTexts, grid=(row, 0), doClone=False) self.removeButton = bottomButtons.buttons[0] for func in ('__init__', 'delete', 'setName'): self.registerNotify(self.updateWindows, 'ccpnmr.Analysis.SpectrumWindow', func) self.update() def destroy(self): for func in ('__init__', 'delete', 'setName'): self.unregisterNotify(self.updateWindows, 'ccpnmr.Analysis.SpectrumWindow', func) BasePopup.destroy(self) def nextSet(self): if self.nextSetFunction: self.nextSetFunction() self.windowPaneNavigate() def prevSet(self): if self.prevSetFunction: self.prevSetFunction() self.windowPaneNavigate() def windowPaneNavigate(self, event=None): if self.followSelect.get() and self.dataFitting: peaks = self.dataFitting.objects if self.windowPane and peaks: peak = peaks[int(len(peaks) / 2)] windowFrame = self.windowPane.getWindowFrame() windowFrame.gotoPeak(peak) if self.markSelect.get(): if self.mark and not self.mark.isDeleted: self.mark.delete() self.mark = createPeakMark(self.dataFitting.refObject) def getWindows(self): peaks = self.scrolledMatrix.objectList windowPanes = [] if peaks: peak = peaks[0] project = peak.root spectrum = peak.peakList.dataSource tryWindows = getActiveWindows(project) for window in tryWindows: for windowPane in window.sortedSpectrumWindowPanes(): if isSpectrumInWindowPane(windowPane, spectrum): windowPanes.append(windowPane) return windowPanes def changeWindow(self, windowPane): if windowPane is not self.windowPane: self.windowPane = windowPane self.windowPaneNavigate() def updateWindows(self, window=None): windowPanes = self.getWindows() index = -1 names = [getWindowPaneName(wp) for wp in windowPanes] windowPane = self.windowPane if windowPanes: if windowPane not in windowPanes: windowPane = windowPanes[0] index = windowPanes.index(windowPane) if windowPane is not self.windowPane: self.windowPane = windowPane self.windowPaneNavigate() self.windowPanePulldown.setup(names, windowPanes, index) def showObject(self): if self.object and (self.object.className == 'Peak'): peaks = [self.object] self.guiParent.guiParent.viewPeaks(peaks) def close(self): BasePopup.close(self) def changeMethod(self, index): if self.dataFitting: self.method = index self.dataFitting.fitFunction = self.method self.updateAfter() def updateMethods(self, *opt): n = len(METHOD_NAMES) if self.method >= n: self.method = 1 self.methodPulldown.setup(METHOD_NAMES, range(n), self.method) def selectObject(self, object, row, col): if object: self.object = object self.updateButtons() def removePoint(self): if self.object and (len(self.dataFitting.objects) > 2): self.dataFitting.objects.remove(self.object) if showYesNo('Query', 'Delete the corresponding %s?' % (self.object.className), parent=self): self.object.delete() self.updateAfter() def draw(self, *junk): title = '%s Function fit %s' % (self.graphTitle, METHOD_NAMES[self.method]) dataSet1 = [] dataSet2 = [] useErr = self.yErrorSelect.isSelected() if not useErr: useErr = None for i in range(len(self.scrolledMatrix.textMatrix)): row = self.scrolledMatrix.textMatrix[i] x = row[1] y = row[2] y2 = row[3] err = useErr and row[5] dataSet1.append([x, y, err]) dataSet2.append([x, y2]) dataSet1.sort() dataSet2.sort() if dataSet1 and dataSet2: drawOriginX = self.xOriginSelect.isSelected() drawOriginY = self.yOriginSelect.isSelected() self.scrolledGraph.update(dataSets=[dataSet1, dataSet2], xLabel=self.xLabel, yLabel=self.yLabel, title=title, drawOriginX=drawOriginX, drawOriginY=drawOriginY) self.scrolledGraph.draw() def updateAfter(self, *object): if self.waiting: return else: self.waiting = True self.after_idle(self.update) def update(self, dataFitting=None, xLabel=None, yLabel=None, graphTitle=None, force=False): if (not force) and dataFitting and (self.dataFitting is dataFitting): if (self.method, self.xLabel, self.yLabel, self.noiseLevel) == \ (self.dataFitting.fitFunction, xLabel, yLabel, self.dataFitting.noiseLevel): self.waiting = False return if dataFitting: self.dataFitting = dataFitting self.xLabel = xLabel or self.xLabel self.yLabel = yLabel or self.yLabel self.graphTitle = graphTitle or self.graphTitle if not self.dataFitting: self.waiting = False return else: dataFitting = self.dataFitting dataFitting = self.getXYfunction(dataFitting) if dataFitting.fitFunction is not None: self.method = dataFitting.fitFunction self.noiseLevel = dataFitting.noiseLevel or self.noiseLevel self.updateMethods() isFitted = dataFitting.fit() textMatrix = [] objectList = [] if isFitted and self.method: methodInfo = getFitMethodInfo()[self.method - 1] textFormat, indices = methodInfo[2] textParams = [dataFitting.parameters[i] or 0 for i in indices] equationText = textFormat % tuple(textParams) errorText = '%4f' % dataFitting.fitError x = dataFitting.dataX y = dataFitting.dataY yFit = dataFitting.fittedY N = len(x) err = hasattr(dataFitting, 'dataErr') and dataFitting.dataErr if not err: err = None # wb104: 10 Mar 2014: not sure why the below was here, it isn't needed #if self.method == 10: # x = [sqrt(v) for v in x] data = [(x[i], y[i], yFit[i], y[i] - yFit[i], dataFitting.objects[i], err and err[i]) for i in range(N)] data.sort() for i in range(N): xi, yi, yFiti, deltai, object, erri = data[i] textMatrix.append([i + 1, xi, yi, yFiti, deltai, erri]) objectList.append(object) else: equationText = 'Equation: <No Fit>' errorText = '<None>' x = dataFitting.dataX y = dataFitting.dataY for i, x in enumerate(x): textMatrix.append([i + 1, x, y[i], None, None, None]) objectList.append(dataFitting.objects[i]) self.equationLabel.set('Equation: %s' % equationText) self.errorLabel.set('Fit Error: %s' % errorText) self.scrolledMatrix.update(textMatrix=textMatrix, objectList=objectList) self.updateWindows() self.updateButtons() self.draw() if self.updateFunction: self.updateFunction(dataFitting) self.waiting = False def updateButtons(self): if self.object: self.removeButton.enable() else: self.removeButton.disable()
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 RegisterPopup(BasePopup): """ **Register User with CCPN** The purpose of this dialog is to allow the user to register with CCPN. This is mainly to allow CCPN to keep track of the number of users, which is important for grant applications. This information is saved both in a file on your computer but also in a private CCPN server database if your computer is connected to the internet. (So unfortunately you will need to register once for each computer you use.) The required information: the user name, organisation and email address. The other information stored on the CCPN server: the version number, the first time and most recent time the server has been notifed. If you are registered, then the server is notified every time a project is opened or saved (if connected to the internet). Click "Register Now" to register, or "Register Later" not to register. """ def __init__(self, parent, isModal=False, *args, **kw): title = 'Project : Register with CCPN' BasePopup.__init__(self, parent=parent, title=title, modal=isModal, **kw) def body(self, guiFrame): self.geometry('600x250+600+250') analysisProfile = self.analysisProfile userName = analysisProfile.userName userOrganisation = analysisProfile.userOrganisation userEmail = analysisProfile.userEmail guiFrame.grid_rowconfigure(0, weight=1) guiFrame.grid_columnconfigure(1, weight=1) explainText = 'To keep track of our users and which versions are being used\n' \ 'we would like you to register your details with us.\n' \ 'Collating the number of users is important for grant applications.\n' \ 'Please do not use accents in any of the information\n' \ 'because Python does not handle it gracefully.' row = 0 label = Label(guiFrame, text=explainText, grid=(row, 0), gridSpan=(1, 2), sticky='ew') row += 1 licenseAgreeText = 'I agree to abide by the rules of the CCPN licensing agreement.' self.agreeButton = CheckButton(guiFrame, licenseAgreeText, tipText=licenseAgreeText) self.agreeButton.grid(row=row, column=1, columnspan=1, sticky='nsew') row += 1 self.entryWidgets = [] for (text, value) in (('Name', userName), ('Organisation', userOrganisation), ('Email', userEmail)): label = Label(guiFrame, text=text + ':', grid=(row, 0)) entry = Entry(guiFrame, text=value or '', grid=(row, 1), sticky='ew', tipText='Your ' + text) self.entryWidgets.append(entry) row += 1 texts = ['Register Now', 'Read License', 'Register Later'] tipTexts = ['Register now', 'Read License', 'Register later'] commands = [self.register, self.openLicense, self.close] buttons = UtilityButtonList(guiFrame, helpUrl=self.help_url, grid=(row, 0), gridSpan=(1, 2), commands=commands, texts=texts, tipTexts=tipTexts) self.buttons = buttons def openLicense(self): licenseUrl = 'http://www.ccpn.ac.uk/license' try: import webbrowser except ImportError: showInfo('License Agreement', 'The CCPN License Agreement is available at %s' % licenseUrl, parent=self) return wb = webbrowser.get() wb.open(licenseUrl) def register(self): if not self.agreeButton.get(): showError( 'License Agreement', 'Please tick the box indicating that you agree to the CCPN licensing terms and conditions.', parent=self) return analysisProfile = self.analysisProfile attrs = ('userName', 'userOrganisation', 'userEmail') for n, entry in enumerate(self.entryWidgets): attr = attrs[n] value = entry.get().strip() if not value: showError('Blank value', '%s is blank, value required' % attr[4:], parent=self) return if attr == 'userEmail' and '@' not in value: showError('Illegal email', 'Email must have "@" in it', parent=self) return try: setattr(analysisProfile, attr, value) except Exception, e: showError('Attribute setting', 'Error setting %s: %s' % (attr[4:], e), parent=self) return analysisProfile.save() try: result = updateRegister(analysisProfile) showInfo('Registering', result, parent=self) self.close() except Exception, e: showError('Registering', str(e), parent=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 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 CingGui(BasePopup): def __init__(self, parent, options, *args, **kw): # Fill in below variable once run generates some results self.haveResults = None # store the options self.options = options BasePopup.__init__(self, parent=parent, title='CING Setup', **kw) # self.setGeometry(850, 750, 50, 50) self.project = None # self.tk_strictMotif( True) self.updateGui() # end def __init__ def body(self, guiFrame): row = 0 col =0 # frame = Frame( guiFrame ) # frame.grid(row=row, column=col, sticky='news') self.menuBar = Menu( guiFrame) self.menuBar.grid( row=row, column=col, sticky='ew') #---------------------------------------------------------------------------------- # Project frame #---------------------------------------------------------------------------------- # guiFrame.grid_columnconfigure(row, weight=1) # frame = LabelFrame(guiFrame, text='Project', font=medFont) row = +1 col =0 frame = LabelFrame(guiFrame, text='Project', **labelFrameAttributes ) print '>', frame.keys() frame.grid(row=row, column=col, sticky='nsew' ) frame.grid_columnconfigure(2, weight=1) # frame.grid_rowconfigure(0, weight=1) srow = 0 self.projectOptions = ['old','new from PDB','new from CCPN','new from CYANA'] self.projOptionsSelect = RadioButtons(frame, selected_index=0, entries=self.projectOptions, direction='vertical', select_callback=self.updateGui ) self.projOptionsSelect.grid(row=srow,column=0,rowspan=len(self.projectOptions),columnspan=2, sticky='w') if self.options.name: text = self.options.name else: text='' # end if self.projEntry = Entry(frame, bd=1, text=text, returnCallback=self.updateGui) self.projEntry.grid(row=srow,column=2,columnspan=2,sticky='ew') # self.projEntry.bind('<Key>', self.updateGui) self.projEntry.bind('<Leave>', self.updateGui) projButton = Button(frame, bd=1,command=self.chooseOldProjectFile, text='browse') projButton.grid(row=srow,column=3,sticky='ew') srow += 1 self.pdbEntry = Entry(frame, bd=1, text='') self.pdbEntry.grid(row=srow,column=2,sticky='ew') self.pdbEntry.bind('<Leave>', self.updateGui) pdbButton = Button(frame, bd=1,command=self.choosePdbFile, text='browse') pdbButton.grid(row=srow,column=3,sticky='ew') srow += 1 self.ccpnEntry = Entry(frame, bd=1, text='') self.ccpnEntry.grid(row=srow,column=2,sticky='ew') self.ccpnEntry.bind('<Leave>', self.updateGui) ccpnButton = Button(frame, bd=1,command=self.chooseCcpnFile, text='browse') ccpnButton.grid(row=srow,column=3,sticky='ew') srow += 1 self.cyanaEntry = Entry(frame, bd=1, text='') self.cyanaEntry.grid(row=srow,column=2,sticky='ew') self.cyanaEntry.bind('<Leave>', self.updateGui) cyanaButton = Button(frame, bd=1,command=self.chooseCyanaFile, text='browse') cyanaButton.grid(row=srow,column=3,sticky='ew') #Empty row srow += 1 label = Label(frame, text='') label.grid(row=srow,column=0,sticky='nw') srow += 1 label = Label(frame, text='Project name:') label.grid(row=srow,column=0,sticky='nw') self.nameEntry = Entry(frame, bd=1, text='') self.nameEntry.grid(row=srow,column=2,sticky='w') #Empty row srow += 1 label = Label(frame, text='') label.grid(row=srow,column=0,sticky='nw') srow += 1 self.openProjectButton = Button(frame, command=self.openProject, text='Open Project', **actionButtonAttributes ) self.openProjectButton.grid(row=srow,column=0, columnspan=4, sticky='ew') #---------------------------------------------------------------------------------- # status #---------------------------------------------------------------------------------- # guiFrame.grid_columnconfigure(1, weight=0) srow = 0 frame = LabelFrame(guiFrame, text='Status', **labelFrameAttributes) frame.grid( row=srow, column=1, sticky='wnes') self.projectStatus = Text(frame, height=11, width=70, borderwidth=0, relief='flat') self.projectStatus.grid(row=0, column=0, sticky='wen') #Empty row srow += 1 label = Label(frame, text='') label.grid(row=srow,column=0,sticky='nw') srow += 1 self.closeProjectButton = Button(frame, command=self.closeProject, text='Close Project', **actionButtonAttributes) self.closeProjectButton.grid(row=srow,column=0, columnspan=4, sticky='ew') #---------------------------------------------------------------------------------- # Validate frame #---------------------------------------------------------------------------------- row +=1 col=0 frame = LabelFrame(guiFrame, text='Validate', **labelFrameAttributes) # frame = LabelFrame(guiFrame, text='Validate', font=medFont) frame.grid(row=row, column=col, sticky='nsew') # frame.grid_columnconfigure(2, weight=1) frame.grid_rowconfigure(0, weight=1) srow = 0 # label = Label(frame, text='validation') # label.grid(row=srow,column=0,sticky='nw') # # self.selectDoValidation = CheckButton(frame) # self.selectDoValidation.grid(row=srow, column=1,sticky='nw' ) # self.selectDoValidation.set(True) # # srow += 1 # label = Label(frame, text='') # label.grid(row=srow,column=0,sticky='nw') # # srow += 1 label = Label(frame, text='checks') label.grid(row=srow,column=0,sticky='nw') self.selectCheckAssign = CheckButton(frame) self.selectCheckAssign.grid(row=srow, column=1,sticky='nw' ) self.selectCheckAssign.set(True) label = Label(frame, text='assignments and shifts') label.grid(row=srow,column=2,sticky='nw') # srow += 1 # self.selectCheckQueen = CheckButton(frame) # self.selectCheckQueen.grid(row=srow, column=4,sticky='nw' ) # self.selectCheckQueen.set(False) # label = Label(frame, text='QUEEN') # label.grid(row=srow,column=5,sticky='nw') # # queenButton = Button(frame, bd=1,command=None, text='setup') # queenButton.grid(row=srow,column=6,sticky='ew') srow += 1 self.selectCheckResraint = CheckButton(frame) self.selectCheckResraint.grid(row=srow, column=1,sticky='nw' ) self.selectCheckResraint.set(True) label = Label(frame, text='restraints') label.grid(row=srow,column=2,sticky='nw') srow += 1 self.selectCheckStructure = CheckButton(frame) self.selectCheckStructure.grid(row=srow, column=1,sticky='nw' ) self.selectCheckStructure.set(True) label = Label(frame, text='structural') label.grid(row=srow,column=2,sticky='nw') srow += 1 self.selectMakeHtml = CheckButton(frame) self.selectMakeHtml.grid(row=srow, column=1,sticky='nw' ) self.selectMakeHtml.set(True) label = Label(frame, text='generate HTML') label.grid(row=srow,column=2,sticky='nw') srow += 1 self.selectCheckScript = CheckButton(frame) self.selectCheckScript.grid(row=srow, column=1,sticky='nw' ) self.selectCheckScript.set(False) label = Label(frame, text='user script') label.grid(row=srow,column=0,sticky='nw') self.validScriptEntry = Entry(frame, bd=1, text='') self.validScriptEntry.grid(row=srow,column=2,columnspan=3, sticky='ew') scriptButton = Button(frame, bd=1,command=self.chooseValidScript, text='browse') scriptButton.grid(row=srow,column=5,sticky='ew') srow += 1 label = Label(frame, text='ranges') label.grid(row=srow,column=0,sticky='nw') self.rangesEntry = Entry( frame, text='' ) self.rangesEntry.grid( row=srow, column=2, columnspan=3, sticky='ew') # self.validScriptEntry = Entry(frame, bd=1, text='') # self.validScriptEntry.grid(row=srow,column=3,sticky='ew') # # scriptButton = Button(frame, bd=1,command=self.chooseValidScript, text='browse') # scriptButton.grid(row=srow,column=4,sticky='ew') srow += 1 texts = ['Run Validation','View Results','Setup QUEEN'] commands = [self.runCing, None, None] buttonBar = ButtonList(frame, texts=texts, commands=commands,expands=True) buttonBar.grid(row=srow, column=0, columnspan=6, sticky='ew') for button in buttonBar.buttons: button.config(**actionButtonAttributes) # end for self.runButton = buttonBar.buttons[0] self.viewResultButton = buttonBar.buttons[1] self.queenButton = buttonBar.buttons[2] #---------------------------------------------------------------------------------- # Miscellaneous frame #---------------------------------------------------------------------------------- row +=0 col=1 # frame = LabelFrame(guiFrame, text='Miscellaneous', font=medFont) frame = LabelFrame(guiFrame, text='Miscellaneous', **labelFrameAttributes) frame.grid(row=row, column=col, sticky='news') frame.grid_columnconfigure(2, weight=1) frame.grid_columnconfigure(4, weight=1,minsize=30) frame.grid_rowconfigure(0, weight=1) # Exports srow = 0 label = Label(frame, text='export to') label.grid(row=srow,column=0,sticky='nw') self.selectExportXeasy = CheckButton(frame) self.selectExportXeasy.grid(row=srow, column=1,sticky='nw' ) self.selectExportXeasy.set(True) label = Label(frame, text='Xeasy, Sparky, TALOS, ...') label.grid(row=srow,column=2,sticky='nw') srow += 1 self.selectExportCcpn = CheckButton(frame) self.selectExportCcpn.grid(row=srow, column=1,sticky='nw' ) self.selectExportCcpn.set(True) label = Label(frame, text='CCPN') label.grid(row=srow,column=2,sticky='nw') srow += 1 self.selectExportQueen = CheckButton(frame) self.selectExportQueen.grid(row=srow, column=1,sticky='nw' ) self.selectExportQueen.set(True) label = Label(frame, text='QUEEN') label.grid(row=srow,column=2,sticky='nw') srow += 1 self.selectExportRefine = CheckButton(frame) self.selectExportRefine.grid(row=srow, column=1,sticky='nw' ) self.selectExportRefine.set(True) label = Label(frame, text='refine') label.grid(row=srow,column=2,sticky='nw') srow += 1 label = Label(frame, text='') label.grid(row=srow,column=0,sticky='nw') # User script srow += 1 label = Label(frame, text='user script') label.grid(row=srow,column=0,sticky='nw') self.selectMiscScript = CheckButton(frame) self.selectMiscScript.grid(row=srow, column=1,sticky='nw' ) self.selectMiscScript.set(False) self.miscScriptEntry = Entry(frame, bd=1, text='') self.miscScriptEntry.grid(row=srow,column=3,sticky='ew') script2Button = Button(frame, bd=1,command=self.chooseMiscScript, text='browse') script2Button.grid(row=srow,column=4,sticky='ew') srow += 1 texts = ['Export','Run Script'] commands = [None, None] buttonBar = ButtonList(frame, texts=texts, commands=commands,expands=True) buttonBar.grid(row=srow, column=0, columnspan=5, sticky='ew') for button in buttonBar.buttons: button.config(**actionButtonAttributes) # end for self.exportButton = buttonBar.buttons[0] self.scriptButton = buttonBar.buttons[1] #---------------------------------------------------------------------------------- # Textarea #---------------------------------------------------------------------------------- row +=1 guiFrame.grid_rowconfigure(row, weight=1) self.outputTextBox = ScrolledText(guiFrame) self.outputTextBox.grid(row=row, column=0, columnspan=2, sticky='nsew') self.redirectConsole() #---------------------------------------------------------------------------------- # Buttons #---------------------------------------------------------------------------------- row +=1 col=0 texts = ['Quit', 'Help'] commands = [self.close, None] self.buttonBar = ButtonList(guiFrame, texts=texts, commands=commands,expands=True) self.buttonBar.grid(row=row, column=col, columnspan=2, sticky='ew') # self.openProjectButton = self.buttonBar.buttons[0] # self.closeProjectButton = self.buttonBar.buttons[1] # self.runButton = self.buttonBar.buttons[0] # self.viewResultButton = self.buttonBar.buttons[1] for button in self.buttonBar.buttons: button.config(**actionButtonAttributes) # end for # end def body def getGuiOptions(self): projectName = self.projEntry.get() index = self.projOptionsSelect.getIndex() if index > 0: makeNewProject = True projectImport = None if index > 1: i = index-2 format = ['PDB','CCPN','CYANA'][i] file = [self.pdbEntry, self.ccpnEntry, self.cyanaEntry][i].get() if not file: showWarning('Failure','No %s file selected' % format) return # end if projectImport = (format, file) # end if else: # Chould also check that any old project file exists makeNewProject = False projectImport = None # end if doValidation = self.selectDoValidation.get() checks = [] if doValidation: if self.selectCheckAssign.get(): checks.append('assignments') # end if if self.selectCheckResraint.get(): checks.append('restraints') # end if if self.selectCheckStructure.get(): checks.append('structural') # end if if self.selectMakeHtml.get(): checks.append('HTML') # end if if self.selectCheckScript.get(): script = self.validScriptEntry.get() if script: checks.append( ('script',script) ) # end if # end if if self.selectCheckQueen.get(): checks.append('queen') # end if # end if exports = [] if self.selectExportXeasy.get(): exports.append('Xeasy') # end if if self.selectExportCcpn.get(): exports.append('CCPN') # end if if self.selectExportQueen.get(): exports.append('QUEEN') # end if if self.selectExportRefine.get(): exports.append('refine') # end if miscScript = None if self.selectMiscScript.get(): script = self.miscScriptEntry.get() if script: miscScript = script # end if # end if return projectName, makeNewProject, projectImport, doValidation, checks, exports, miscScript # end def getGuiOptions def runCing(self): options = self.getGuiOptions() if options: projectName, makeNewProject, projectImport, doValidation, checks, exports, miscScript = options print 'Project name:', projectName print 'Make new project?', makeNewProject print 'Import source:', projectImport print 'Do vailidation?', doValidation print 'Validation checks:', ','.join(checks) print 'Export to:', ','.join(exports) print 'User script:', miscScript # end if # end def runCing # else there was already an error message def chooseOldProjectFile(self): fileTypes = [ FileType('CING', ['project.xml']), FileType('All', ['*']) ] popup = FileSelectPopup(self, file_types = fileTypes, title = 'Select CING project file', dismiss_text = 'Cancel', selected_file_must_exist = True) fileName = popup.getFile() # dirName = popup.getDirectory() if len(fileName) > 0: # Put text into entry,name widgets dummy,name = cing.Project.rootPath(fileName) self.projEntry.configure(state='normal') self.projEntry.set(fileName) self.nameEntry.configure(state='normal') self.nameEntry.set(name) self.nameEntry.configure(state='disabled') # choose the correct radiobutton self.projOptionsSelect.setIndex(0) self.updateGui() # end if #nd if popup.destroy() # end def chooseOldProjectFile def choosePdbFile(self): fileTypes = [ FileType('PDB', ['*.pdb']), FileType('All', ['*'])] popup = FileSelectPopup(self, file_types = fileTypes, title = 'PDB file', dismiss_text = 'Cancel', selected_file_must_exist = True) fileName = popup.getFile() if len(fileName)>0: # Put text into entry widget self.pdbEntry.configure(state='normal') self.pdbEntry.set(fileName) # Put text into name widget _dir,name,dummy = nTpath( fileName ) self.nameEntry.configure(state='normal') self.nameEntry.set(name) # choose the correct radiobutton self.projOptionsSelect.setIndex(1) self.updateGui() #end if popup.destroy() # end def choosePdbFile def chooseCcpnFile(self): fileTypes = [ FileType('XML', ['*.xml']), FileType('All', ['*'])] popup = FileSelectPopup(self, file_types = fileTypes, title = 'CCPN project XML file', dismiss_text = 'Cancel', selected_file_must_exist = True) fileName = popup.getFile() if len(fileName)>0: self.pdbEntry.configure(state='normal') self.pdbEntry.set(fileName) self.projOptionsSelect.setIndex(1) _dir,name,dummy = nTpath( fileName ) self.nameEntry.set(name) #end if self.ccpnEntry.set(fileName) self.projOptionsSelect.setIndex(2) popup.destroy() # end def chooseCcpnFile def chooseCyanaFile(self): # Prepend default Cyana file extension below fileTypes = [ FileType('All', ['*']), ] popup = FileSelectPopup(self, file_types = fileTypes, title = 'CYANA fproject file', dismiss_text = 'Cancel', selected_file_must_exist = True) fileName = popup.getFile() self.cyanaEntry.set(fileName) self.projOptionsSelect.setIndex(3) popup.destroy() # end def chooseCyanaFile def chooseValidScript(self): # Prepend default Cyana file extension below fileTypes = [ FileType('All', ['*']), ] popup = FileSelectPopup(self, file_types = fileTypes, title = 'Script file', dismiss_text = 'Cancel', selected_file_must_exist = True) fileName = popup.getFile() self.validScriptEntry.set(fileName) popup.destroy() # end def chooseValidScript def chooseMiscScript(self): # Prepend default Cyana file extension below fileTypes = [ FileType('All', ['*']), ] popup = FileSelectPopup(self, file_types = fileTypes, title = 'Script file', dismiss_text = 'Cancel', selected_file_must_exist = True) fileName = popup.getFile() self.miscScriptEntry.set(fileName) popup.destroy() # end def chooseMiscScript def openProject(self ): projOption = self.projOptionsSelect.get() if projOption == self.projectOptions[0]: self.openOldProject() elif projOption == self.projectOptions[1]: self.initPdb() # end if if self.project: self.project.gui = self # end if self.updateGui() #end def def openOldProject(self ): fName = self.projEntry.get() if not os.path.exists( fName ): nTerror('Error: file "%s" does not exist\n', fName) #end if if self.project: self.closeProject() # end if self.project = cing.Project.open( name=fName, status='old', verbose=False ) #end def def initPdb(self ): fName = self.pdbEntry.get() if not os.path.exists( fName ): nTerror('Error: file "%s" does not exist\n', fName) #end if self.project = cing.Project.open( self.nameEntry.get(), status='new' ) self.project.initPDB( pdbFile=fName, convention = 'PDB' ) #end def def closeProject(self): if self.project: self.project.close() # end if self.project = None self.updateGui() #end def def updateGui(self, event=None): projOption = self.projOptionsSelect.get() buttons = self.buttonBar.buttons # Disable entries for e in [self.projEntry, self.pdbEntry, self.ccpnEntry, self.cyanaEntry, self.nameEntry]: e.configure(state='disabled') #end for if projOption == self.projectOptions[0]: # Enable entries self.projEntry.configure(state='normal') if (len(self.projEntry.get()) > 0): self.openProjectButton.enable() self.runButton.enable() else: self.openProjectButton.disable() self.runButton.disable() #end if elif projOption == self.projectOptions[1]: # Enable entries self.pdbEntry.configure(state='normal') self.nameEntry.configure(state='normal') if (len(self.pdbEntry.get()) > 0 and len(self.nameEntry.get()) > 0): buttons[0].enable() buttons[1].enable() else: buttons[0].disable() buttons[1].disable() #end if #end if elif projOption == self.projectOptions[2]: # Enable entries self.ccpnEntry.configure(state='normal') self.nameEntry.configure(state='normal') if (len(self.ccpnEntry.get()) > 0 and len(self.nameEntry.get()) > 0): buttons[0].enable() buttons[1].enable() else: buttons[0].disable() buttons[1].disable() #end if #end if elif projOption == self.projectOptions[3]: # Enable entries self.cyanaEntry.configure(state='normal') self.nameEntry.configure(state='normal') if (len(self.cyanaEntry.get()) > 0 and len(self.nameEntry.get()) > 0): buttons[0].enable() buttons[1].enable() else: buttons[0].disable() buttons[1].disable() #end if #end if self.projectStatus.clear() if not self.project: self.projectStatus.setText('No open project') self.closeProjectButton.setText('Close Project') self.closeProjectButton.disable() else: self.projectStatus.setText(self.project.format()) self.closeProjectButton.enable() self.closeProjectButton.setText(sprintf('Close Project "%s"', self.project.name)) #end if #end def def redirectConsole(self): #pipe = TextPipe(self.inputTextBox.text_area) #sys.stdin = pipe pipe = TextPipe(self.outputTextBox.text_area) sys.stdout = pipe nTmessage.stream = pipe # end def redirectConsole # sys.stderr = pipe def resetConsole(self): #sys.stdin = stdin nTmessage.stream = stdout sys.stdout = stdout sys.stderr = stderr # end def resetConsole def open(self): self.redirectConsole() BasePopup.open(self) # end def open def close(self): geometry = self.getGeometry() self.resetConsole() BasePopup.close(self) print 'close:',geometry sys.exit(0) # remove later # end def close def destroy(self): geometry = self.getGeometry() self.resetConsole() BasePopup.destroy(self) print 'destroy:',geometry sys.exit(0) # remove later
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 TranslatePeakPopup(BasePopup): def __init__(self, parent, fixedPeak, movePeak, *args, **kw): self.movePeakDim = None self.fixedPeak = fixedPeak self.movePeak = movePeak self.setupDimMapping() BasePopup.__init__(self, parent=parent, title='Reference By Peak', modal=True, *args, **kw) def body(self, guiFrame): fixedSpectrum = self.fixedPeak.peakList.dataSource moveSpectrum = self.movePeak.peakList.dataSource guiFrame.grid_columnconfigure(0, weight=1) row = 0 frame = LabelFrame(guiFrame, text='Working Spectra', grid=(row, 0), sticky='ew') frame.grid_columnconfigure(1, weight=1) s = '%s:%-20s' % (fixedSpectrum.experiment.name, fixedSpectrum.name) Label(frame, text='Fixed:', grid=(0, 0), sticky='e') tipText = 'The experiment:spectrum name of the reference spectrum, which will not be moved' Label(frame, text=s, grid=(0, 1), tipText=tipText) s = '%s:%-20s' % (moveSpectrum.experiment.name, moveSpectrum.name) Label(frame, text='Moving:', grid=(0, 2), sticky='e') tipText = 'The experiment:spectrum name of the spectrum which will be re-referenced using peak positions' Label(frame, text=s, grid=(0, 3), tipText=tipText) row += 1 guiFrame.grid_rowconfigure(row, weight=1) frame = LabelFrame(guiFrame, text='Peak Position Mapping', grid=(row, 0)) frame.expandGrid(0, 0) tipText = 'Whether the more than one moving spectrum dimension may be mapped to the same fixed spectrum dimension; typically not used but can be useful for re-referencing with a diagonal peak' self.fixedDimsButton = CheckButton( frame, text='Allow repeated fixed dim', selected=False, grid=(0, 0), callback=self.changedFixedDimsButton, tipText=tipText) self.dimPulldown = PulldownList(self, callback=self.setDimMapping) editWidgets = [None, None, self.dimPulldown, None, None, None] editGetCallbacks = [None, None, self.getDimMapping, None, None, None] editSetCallbacks = [None, None, self.setDimMapping, None, None, None] tipTexts = [ 'The dimension number of the spectrum being re-referenced', 'The isotope type that corresponds to both the fixed and re-reference spectrum dimensions', 'The dimension number of the fixed spectrum, used as a reference', 'The position of the moving peak in the relevant dimension, before any changes (for the spectrum being re-referenced)', 'The position of the fixed peak in the relevant dimension; potential new position of the moving peak after re-referencing changes', 'The chemical shift difference between the two peak positions on one dimension' ] headingList = [ 'Moving\nDim', 'Isotope', 'Fixed\nDim', 'Original\nPosition', 'New\nPosition', u'\u0394' ] self.scrolledMatrix = ScrolledMatrix(frame, headingList=headingList, callback=self.selectPeakDim, editWidgets=editWidgets, maxRows=5, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, grid=(1, 0), tipTexts=tipTexts) row += 1 tipTexts = [ 'Use the stated mapping of dimensions to re-reference the moving spectrum so that the selected peaks lie at the same position', ] texts = ['Commit'] commands = [self.ok] buttons = UtilityButtonList(guiFrame, texts=texts, commands=commands, closeText='Cancel', helpUrl=self.help_url, grid=(row, 0), tipTexts=tipTexts) self.update() def getDataDimIsotopes(self, dataDim): isotopes = [] if dataDim.className == 'FreqDataDim': for dataDimRef in dataDim.dataDimRefs: for isotopeCode in dataDimRef.expDimRef.isotopeCodes: isotopes.append(isotopeCode) return isotopes def setupDimMapping(self): self.dimMapping = {} usedDataDims = set() for movePeakDim in self.movePeak.sortedPeakDims(): fixedDataDims = self.getMatchingDataDims(movePeakDim) fixedDataDims = [ dataDim for dataDim in fixedDataDims if dataDim not in usedDataDims ] if fixedDataDims: moveDataDim = movePeakDim.dataDim fixedDataDim = fixedDataDims[0] self.dimMapping[moveDataDim.dim] = fixedDataDim.dim usedDataDims.add(fixedDataDim) def getMatchingDataDims(self, movePeakDim): moveIsotopes = self.getDataDimIsotopes(movePeakDim.dataDim) fixedDataDims = [] for fixedPeakDim in self.fixedPeak.sortedPeakDims(): fixedDataDim = fixedPeakDim.dataDim isotopes = self.getDataDimIsotopes(fixedDataDim) if isotopes == moveIsotopes: fixedDataDims.append(fixedDataDim) return fixedDataDims def getDimMapping(self, movePeakDim): fixedDataDims = [ None, ] fixedDataDims.extend(self.getMatchingDataDims(movePeakDim)) possibleDims = [('%d' % fixedDataDim.dim if fixedDataDim else 'None') for fixedDataDim in fixedDataDims] mappedDim = self.dimMapping.get(movePeakDim.dim) if mappedDim: try: # SF bug 3137169 had problem with string not being in list # not sure why that might be happening but play safe index = possibleDims.index(str(mappedDim)) except: index = 0 else: index = 0 self.dimPulldown.setup(possibleDims, fixedDataDims, index) def setDimMapping(self, fixedDataDim): if fixedDataDim and fixedDataDim.className == 'PeakDim': # some kind of bug in ScrolledMatrix which means that this being called with table object # instead of menu object when you click outside of menu to stop editing return # fixedDataDim is the new suggested dataDim which self.movePeakDim.dataDim gets mapped to movePeakDim = self.movePeakDim moveDim = movePeakDim.dim if fixedDataDim is None: fixedDimNew = None else: fixedDimNew = fixedDataDim.dim fixedDimOld = self.dimMapping.get(moveDim) if fixedDimNew: if not self.fixedDimsButton.get(): # do not allow repeated fixed dims, so need to swap things around # old: movePeakDim --> fixedDimOld, ??? --> fixedDimNew # new: movePeakDim --> fixedDimNew, ??? --> fixedDimOld # do not have to do anything if fixedDimNew does not appear (i.e. ??? non-existent) for moveDim2 in self.dimMapping: if self.dimMapping[moveDim2] == fixedDimNew: self.dimMapping[moveDim2] = fixedDimOld break self.dimMapping[moveDim] = fixedDimNew elif moveDim in self.dimMapping: del self.dimMapping[moveDim] self.update() def changedFixedDimsButton(self, selected): if not selected: keys = self.dimMapping.keys() keys.sort() usedSet = set() for moveDim in keys: fixedDim = self.dimMapping[moveDim] if fixedDim in usedSet: del self.dimMapping[moveDim] else: usedSet.add(fixedDim) self.update() def selectPeakDim(self, obj, row, col): self.movePeakDim = obj def update(self): textMatrix = [] objectList = [] for peakDim in self.movePeak.sortedPeakDims(): dataDim = peakDim.dataDim if dataDim.className != 'FreqDataDim': continue dim = peakDim.dim isotopes = self.getDataDimIsotopes(dataDim) selectedDim = self.dimMapping.get(dim) refValue = delta = None if selectedDim: peakDim0 = self.fixedPeak.findFirstPeakDim(dim=selectedDim) if peakDim0: refValue = '%4.3f' % peakDim0.value delta = '%4.3f' % (peakDim0.value - peakDim.value) else: selectedDim = 'None' datum = [ dim, ','.join(isotopes), selectedDim, peakDim.value, refValue, delta ] textMatrix.append(datum) objectList.append(peakDim) self.scrolledMatrix.update(objectList=objectList, textMatrix=textMatrix) def apply(self): # 8 Jun 2015: looks like sometimes get dims mapped to None, which causes translate to go wrong # below is not fixing problem at source but it does mean the translate should work keys = self.dimMapping.keys() for key in keys: if self.dimMapping[key] is None: del self.dimMapping[key] translateSpectrumUsingPeaks(self.fixedPeak, self.movePeak, self.dimMapping) self.update() return True
class CingGui(BasePopup): def __init__(self, parent, options, *args, **kw): # Fill in below variable once run generates some results self.haveResults = None # store the options self.options = options BasePopup.__init__(self, parent=parent, title='CING Setup', **kw) # self.setGeometry(850, 750, 50, 50) self.project = None # self.tk_strictMotif( True) self.updateGui() # end def __init__ def body(self, guiFrame): row = 0 col = 0 # frame = Frame( guiFrame ) # frame.grid(row=row, column=col, sticky='news') self.menuBar = Menu(guiFrame) self.menuBar.grid(row=row, column=col, sticky='ew') #---------------------------------------------------------------------------------- # Project frame #---------------------------------------------------------------------------------- # guiFrame.grid_columnconfigure(row, weight=1) # frame = LabelFrame(guiFrame, text='Project', font=medFont) row = +1 col = 0 frame = LabelFrame(guiFrame, text='Project', **labelFrameAttributes) print '>', frame.keys() frame.grid(row=row, column=col, sticky='nsew') frame.grid_columnconfigure(2, weight=1) # frame.grid_rowconfigure(0, weight=1) srow = 0 self.projectOptions = [ 'old', 'new from PDB', 'new from CCPN', 'new from CYANA' ] self.projOptionsSelect = RadioButtons(frame, selected_index=0, entries=self.projectOptions, direction='vertical', select_callback=self.updateGui) self.projOptionsSelect.grid(row=srow, column=0, rowspan=len(self.projectOptions), columnspan=2, sticky='w') if self.options.name: text = self.options.name else: text = '' # end if self.projEntry = Entry(frame, bd=1, text=text, returnCallback=self.updateGui) self.projEntry.grid(row=srow, column=2, columnspan=2, sticky='ew') # self.projEntry.bind('<Key>', self.updateGui) self.projEntry.bind('<Leave>', self.updateGui) projButton = Button(frame, bd=1, command=self.chooseOldProjectFile, text='browse') projButton.grid(row=srow, column=3, sticky='ew') srow += 1 self.pdbEntry = Entry(frame, bd=1, text='') self.pdbEntry.grid(row=srow, column=2, sticky='ew') self.pdbEntry.bind('<Leave>', self.updateGui) pdbButton = Button(frame, bd=1, command=self.choosePdbFile, text='browse') pdbButton.grid(row=srow, column=3, sticky='ew') srow += 1 self.ccpnEntry = Entry(frame, bd=1, text='') self.ccpnEntry.grid(row=srow, column=2, sticky='ew') self.ccpnEntry.bind('<Leave>', self.updateGui) ccpnButton = Button(frame, bd=1, command=self.chooseCcpnFile, text='browse') ccpnButton.grid(row=srow, column=3, sticky='ew') srow += 1 self.cyanaEntry = Entry(frame, bd=1, text='') self.cyanaEntry.grid(row=srow, column=2, sticky='ew') self.cyanaEntry.bind('<Leave>', self.updateGui) cyanaButton = Button(frame, bd=1, command=self.chooseCyanaFile, text='browse') cyanaButton.grid(row=srow, column=3, sticky='ew') #Empty row srow += 1 label = Label(frame, text='') label.grid(row=srow, column=0, sticky='nw') srow += 1 label = Label(frame, text='Project name:') label.grid(row=srow, column=0, sticky='nw') self.nameEntry = Entry(frame, bd=1, text='') self.nameEntry.grid(row=srow, column=2, sticky='w') #Empty row srow += 1 label = Label(frame, text='') label.grid(row=srow, column=0, sticky='nw') srow += 1 self.openProjectButton = Button(frame, command=self.openProject, text='Open Project', **actionButtonAttributes) self.openProjectButton.grid(row=srow, column=0, columnspan=4, sticky='ew') #---------------------------------------------------------------------------------- # status #---------------------------------------------------------------------------------- # guiFrame.grid_columnconfigure(1, weight=0) srow = 0 frame = LabelFrame(guiFrame, text='Status', **labelFrameAttributes) frame.grid(row=srow, column=1, sticky='wnes') self.projectStatus = Text(frame, height=11, width=70, borderwidth=0, relief='flat') self.projectStatus.grid(row=0, column=0, sticky='wen') #Empty row srow += 1 label = Label(frame, text='') label.grid(row=srow, column=0, sticky='nw') srow += 1 self.closeProjectButton = Button(frame, command=self.closeProject, text='Close Project', **actionButtonAttributes) self.closeProjectButton.grid(row=srow, column=0, columnspan=4, sticky='ew') #---------------------------------------------------------------------------------- # Validate frame #---------------------------------------------------------------------------------- row += 1 col = 0 frame = LabelFrame(guiFrame, text='Validate', **labelFrameAttributes) # frame = LabelFrame(guiFrame, text='Validate', font=medFont) frame.grid(row=row, column=col, sticky='nsew') # frame.grid_columnconfigure(2, weight=1) frame.grid_rowconfigure(0, weight=1) srow = 0 # label = Label(frame, text='validation') # label.grid(row=srow,column=0,sticky='nw') # # self.selectDoValidation = CheckButton(frame) # self.selectDoValidation.grid(row=srow, column=1,sticky='nw' ) # self.selectDoValidation.set(True) # # srow += 1 # label = Label(frame, text='') # label.grid(row=srow,column=0,sticky='nw') # # srow += 1 label = Label(frame, text='checks') label.grid(row=srow, column=0, sticky='nw') self.selectCheckAssign = CheckButton(frame) self.selectCheckAssign.grid(row=srow, column=1, sticky='nw') self.selectCheckAssign.set(True) label = Label(frame, text='assignments and shifts') label.grid(row=srow, column=2, sticky='nw') # srow += 1 # self.selectCheckQueen = CheckButton(frame) # self.selectCheckQueen.grid(row=srow, column=4,sticky='nw' ) # self.selectCheckQueen.set(False) # label = Label(frame, text='QUEEN') # label.grid(row=srow,column=5,sticky='nw') # # queenButton = Button(frame, bd=1,command=None, text='setup') # queenButton.grid(row=srow,column=6,sticky='ew') srow += 1 self.selectCheckResraint = CheckButton(frame) self.selectCheckResraint.grid(row=srow, column=1, sticky='nw') self.selectCheckResraint.set(True) label = Label(frame, text='restraints') label.grid(row=srow, column=2, sticky='nw') srow += 1 self.selectCheckStructure = CheckButton(frame) self.selectCheckStructure.grid(row=srow, column=1, sticky='nw') self.selectCheckStructure.set(True) label = Label(frame, text='structural') label.grid(row=srow, column=2, sticky='nw') srow += 1 self.selectMakeHtml = CheckButton(frame) self.selectMakeHtml.grid(row=srow, column=1, sticky='nw') self.selectMakeHtml.set(True) label = Label(frame, text='generate HTML') label.grid(row=srow, column=2, sticky='nw') srow += 1 self.selectCheckScript = CheckButton(frame) self.selectCheckScript.grid(row=srow, column=1, sticky='nw') self.selectCheckScript.set(False) label = Label(frame, text='user script') label.grid(row=srow, column=0, sticky='nw') self.validScriptEntry = Entry(frame, bd=1, text='') self.validScriptEntry.grid(row=srow, column=2, columnspan=3, sticky='ew') scriptButton = Button(frame, bd=1, command=self.chooseValidScript, text='browse') scriptButton.grid(row=srow, column=5, sticky='ew') srow += 1 label = Label(frame, text='ranges') label.grid(row=srow, column=0, sticky='nw') self.rangesEntry = Entry(frame, text='') self.rangesEntry.grid(row=srow, column=2, columnspan=3, sticky='ew') # self.validScriptEntry = Entry(frame, bd=1, text='') # self.validScriptEntry.grid(row=srow,column=3,sticky='ew') # # scriptButton = Button(frame, bd=1,command=self.chooseValidScript, text='browse') # scriptButton.grid(row=srow,column=4,sticky='ew') srow += 1 texts = ['Run Validation', 'View Results', 'Setup QUEEN'] commands = [self.runCing, None, None] buttonBar = ButtonList(frame, texts=texts, commands=commands, expands=True) buttonBar.grid(row=srow, column=0, columnspan=6, sticky='ew') for button in buttonBar.buttons: button.config(**actionButtonAttributes) # end for self.runButton = buttonBar.buttons[0] self.viewResultButton = buttonBar.buttons[1] self.queenButton = buttonBar.buttons[2] #---------------------------------------------------------------------------------- # Miscellaneous frame #---------------------------------------------------------------------------------- row += 0 col = 1 # frame = LabelFrame(guiFrame, text='Miscellaneous', font=medFont) frame = LabelFrame(guiFrame, text='Miscellaneous', **labelFrameAttributes) frame.grid(row=row, column=col, sticky='news') frame.grid_columnconfigure(2, weight=1) frame.grid_columnconfigure(4, weight=1, minsize=30) frame.grid_rowconfigure(0, weight=1) # Exports srow = 0 label = Label(frame, text='export to') label.grid(row=srow, column=0, sticky='nw') self.selectExportXeasy = CheckButton(frame) self.selectExportXeasy.grid(row=srow, column=1, sticky='nw') self.selectExportXeasy.set(True) label = Label(frame, text='Xeasy, Sparky, TALOS, ...') label.grid(row=srow, column=2, sticky='nw') srow += 1 self.selectExportCcpn = CheckButton(frame) self.selectExportCcpn.grid(row=srow, column=1, sticky='nw') self.selectExportCcpn.set(True) label = Label(frame, text='CCPN') label.grid(row=srow, column=2, sticky='nw') srow += 1 self.selectExportQueen = CheckButton(frame) self.selectExportQueen.grid(row=srow, column=1, sticky='nw') self.selectExportQueen.set(True) label = Label(frame, text='QUEEN') label.grid(row=srow, column=2, sticky='nw') srow += 1 self.selectExportRefine = CheckButton(frame) self.selectExportRefine.grid(row=srow, column=1, sticky='nw') self.selectExportRefine.set(True) label = Label(frame, text='refine') label.grid(row=srow, column=2, sticky='nw') srow += 1 label = Label(frame, text='') label.grid(row=srow, column=0, sticky='nw') # User script srow += 1 label = Label(frame, text='user script') label.grid(row=srow, column=0, sticky='nw') self.selectMiscScript = CheckButton(frame) self.selectMiscScript.grid(row=srow, column=1, sticky='nw') self.selectMiscScript.set(False) self.miscScriptEntry = Entry(frame, bd=1, text='') self.miscScriptEntry.grid(row=srow, column=3, sticky='ew') script2Button = Button(frame, bd=1, command=self.chooseMiscScript, text='browse') script2Button.grid(row=srow, column=4, sticky='ew') srow += 1 texts = ['Export', 'Run Script'] commands = [None, None] buttonBar = ButtonList(frame, texts=texts, commands=commands, expands=True) buttonBar.grid(row=srow, column=0, columnspan=5, sticky='ew') for button in buttonBar.buttons: button.config(**actionButtonAttributes) # end for self.exportButton = buttonBar.buttons[0] self.scriptButton = buttonBar.buttons[1] #---------------------------------------------------------------------------------- # Textarea #---------------------------------------------------------------------------------- row += 1 guiFrame.grid_rowconfigure(row, weight=1) self.outputTextBox = ScrolledText(guiFrame) self.outputTextBox.grid(row=row, column=0, columnspan=2, sticky='nsew') self.redirectConsole() #---------------------------------------------------------------------------------- # Buttons #---------------------------------------------------------------------------------- row += 1 col = 0 texts = ['Quit', 'Help'] commands = [self.close, None] self.buttonBar = ButtonList(guiFrame, texts=texts, commands=commands, expands=True) self.buttonBar.grid(row=row, column=col, columnspan=2, sticky='ew') # self.openProjectButton = self.buttonBar.buttons[0] # self.closeProjectButton = self.buttonBar.buttons[1] # self.runButton = self.buttonBar.buttons[0] # self.viewResultButton = self.buttonBar.buttons[1] for button in self.buttonBar.buttons: button.config(**actionButtonAttributes) # end for # end def body def getGuiOptions(self): projectName = self.projEntry.get() index = self.projOptionsSelect.getIndex() if index > 0: makeNewProject = True projectImport = None if index > 1: i = index - 2 format = ['PDB', 'CCPN', 'CYANA'][i] file = [self.pdbEntry, self.ccpnEntry, self.cyanaEntry][i].get() if not file: showWarning('Failure', 'No %s file selected' % format) return # end if projectImport = (format, file) # end if else: # Chould also check that any old project file exists makeNewProject = False projectImport = None # end if doValidation = self.selectDoValidation.get() checks = [] if doValidation: if self.selectCheckAssign.get(): checks.append('assignments') # end if if self.selectCheckResraint.get(): checks.append('restraints') # end if if self.selectCheckStructure.get(): checks.append('structural') # end if if self.selectMakeHtml.get(): checks.append('HTML') # end if if self.selectCheckScript.get(): script = self.validScriptEntry.get() if script: checks.append(('script', script)) # end if # end if if self.selectCheckQueen.get(): checks.append('queen') # end if # end if exports = [] if self.selectExportXeasy.get(): exports.append('Xeasy') # end if if self.selectExportCcpn.get(): exports.append('CCPN') # end if if self.selectExportQueen.get(): exports.append('QUEEN') # end if if self.selectExportRefine.get(): exports.append('refine') # end if miscScript = None if self.selectMiscScript.get(): script = self.miscScriptEntry.get() if script: miscScript = script # end if # end if return projectName, makeNewProject, projectImport, doValidation, checks, exports, miscScript # end def getGuiOptions def runCing(self): options = self.getGuiOptions() if options: projectName, makeNewProject, projectImport, doValidation, checks, exports, miscScript = options print 'Project name:', projectName print 'Make new project?', makeNewProject print 'Import source:', projectImport print 'Do vailidation?', doValidation print 'Validation checks:', ','.join(checks) print 'Export to:', ','.join(exports) print 'User script:', miscScript # end if # end def runCing # else there was already an error message def chooseOldProjectFile(self): fileTypes = [FileType('CING', ['project.xml']), FileType('All', ['*'])] popup = FileSelectPopup(self, file_types=fileTypes, title='Select CING project file', dismiss_text='Cancel', selected_file_must_exist=True) fileName = popup.getFile() # dirName = popup.getDirectory() if len(fileName) > 0: # Put text into entry,name widgets dummy, name = cing.Project.rootPath(fileName) self.projEntry.configure(state='normal') self.projEntry.set(fileName) self.nameEntry.configure(state='normal') self.nameEntry.set(name) self.nameEntry.configure(state='disabled') # choose the correct radiobutton self.projOptionsSelect.setIndex(0) self.updateGui() # end if #nd if popup.destroy() # end def chooseOldProjectFile def choosePdbFile(self): fileTypes = [FileType('PDB', ['*.pdb']), FileType('All', ['*'])] popup = FileSelectPopup(self, file_types=fileTypes, title='PDB file', dismiss_text='Cancel', selected_file_must_exist=True) fileName = popup.getFile() if len(fileName) > 0: # Put text into entry widget self.pdbEntry.configure(state='normal') self.pdbEntry.set(fileName) # Put text into name widget _dir, name, dummy = nTpath(fileName) self.nameEntry.configure(state='normal') self.nameEntry.set(name) # choose the correct radiobutton self.projOptionsSelect.setIndex(1) self.updateGui() #end if popup.destroy() # end def choosePdbFile def chooseCcpnFile(self): fileTypes = [FileType('XML', ['*.xml']), FileType('All', ['*'])] popup = FileSelectPopup(self, file_types=fileTypes, title='CCPN project XML file', dismiss_text='Cancel', selected_file_must_exist=True) fileName = popup.getFile() if len(fileName) > 0: self.pdbEntry.configure(state='normal') self.pdbEntry.set(fileName) self.projOptionsSelect.setIndex(1) _dir, name, dummy = nTpath(fileName) self.nameEntry.set(name) #end if self.ccpnEntry.set(fileName) self.projOptionsSelect.setIndex(2) popup.destroy() # end def chooseCcpnFile def chooseCyanaFile(self): # Prepend default Cyana file extension below fileTypes = [ FileType('All', ['*']), ] popup = FileSelectPopup(self, file_types=fileTypes, title='CYANA fproject file', dismiss_text='Cancel', selected_file_must_exist=True) fileName = popup.getFile() self.cyanaEntry.set(fileName) self.projOptionsSelect.setIndex(3) popup.destroy() # end def chooseCyanaFile def chooseValidScript(self): # Prepend default Cyana file extension below fileTypes = [ FileType('All', ['*']), ] popup = FileSelectPopup(self, file_types=fileTypes, title='Script file', dismiss_text='Cancel', selected_file_must_exist=True) fileName = popup.getFile() self.validScriptEntry.set(fileName) popup.destroy() # end def chooseValidScript def chooseMiscScript(self): # Prepend default Cyana file extension below fileTypes = [ FileType('All', ['*']), ] popup = FileSelectPopup(self, file_types=fileTypes, title='Script file', dismiss_text='Cancel', selected_file_must_exist=True) fileName = popup.getFile() self.miscScriptEntry.set(fileName) popup.destroy() # end def chooseMiscScript def openProject(self): projOption = self.projOptionsSelect.get() if projOption == self.projectOptions[0]: self.openOldProject() elif projOption == self.projectOptions[1]: self.initPdb() # end if if self.project: self.project.gui = self # end if self.updateGui() #end def def openOldProject(self): fName = self.projEntry.get() if not os.path.exists(fName): nTerror('Error: file "%s" does not exist\n', fName) #end if if self.project: self.closeProject() # end if self.project = cing.Project.open(name=fName, status='old', verbose=False) #end def def initPdb(self): fName = self.pdbEntry.get() if not os.path.exists(fName): nTerror('Error: file "%s" does not exist\n', fName) #end if self.project = cing.Project.open(self.nameEntry.get(), status='new') self.project.initPDB(pdbFile=fName, convention='PDB') #end def def closeProject(self): if self.project: self.project.close() # end if self.project = None self.updateGui() #end def def updateGui(self, event=None): projOption = self.projOptionsSelect.get() buttons = self.buttonBar.buttons # Disable entries for e in [ self.projEntry, self.pdbEntry, self.ccpnEntry, self.cyanaEntry, self.nameEntry ]: e.configure(state='disabled') #end for if projOption == self.projectOptions[0]: # Enable entries self.projEntry.configure(state='normal') if (len(self.projEntry.get()) > 0): self.openProjectButton.enable() self.runButton.enable() else: self.openProjectButton.disable() self.runButton.disable() #end if elif projOption == self.projectOptions[1]: # Enable entries self.pdbEntry.configure(state='normal') self.nameEntry.configure(state='normal') if (len(self.pdbEntry.get()) > 0 and len(self.nameEntry.get()) > 0): buttons[0].enable() buttons[1].enable() else: buttons[0].disable() buttons[1].disable() #end if #end if elif projOption == self.projectOptions[2]: # Enable entries self.ccpnEntry.configure(state='normal') self.nameEntry.configure(state='normal') if (len(self.ccpnEntry.get()) > 0 and len(self.nameEntry.get()) > 0): buttons[0].enable() buttons[1].enable() else: buttons[0].disable() buttons[1].disable() #end if #end if elif projOption == self.projectOptions[3]: # Enable entries self.cyanaEntry.configure(state='normal') self.nameEntry.configure(state='normal') if (len(self.cyanaEntry.get()) > 0 and len(self.nameEntry.get()) > 0): buttons[0].enable() buttons[1].enable() else: buttons[0].disable() buttons[1].disable() #end if #end if self.projectStatus.clear() if not self.project: self.projectStatus.setText('No open project') self.closeProjectButton.setText('Close Project') self.closeProjectButton.disable() else: self.projectStatus.setText(self.project.format()) self.closeProjectButton.enable() self.closeProjectButton.setText( sprintf('Close Project "%s"', self.project.name)) #end if #end def def redirectConsole(self): #pipe = TextPipe(self.inputTextBox.text_area) #sys.stdin = pipe pipe = TextPipe(self.outputTextBox.text_area) sys.stdout = pipe nTmessage.stream = pipe # end def redirectConsole # sys.stderr = pipe def resetConsole(self): #sys.stdin = stdin nTmessage.stream = stdout sys.stdout = stdout sys.stderr = stderr # end def resetConsole def open(self): self.redirectConsole() BasePopup.open(self) # end def open def close(self): geometry = self.getGeometry() self.resetConsole() BasePopup.close(self) print 'close:', geometry sys.exit(0) # remove later # end def close def destroy(self): geometry = self.getGeometry() self.resetConsole() BasePopup.destroy(self) print 'destroy:', geometry sys.exit(0) # remove later
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 ViewChemicalShiftsPopup(BasePopup): """ **A Table of Chemical Shifts for Export** This section is designed to make a layout of a table for chemical shifts on a per-residue basis which may them be exported as either PostScript, for printing and graphical manipulation, or as plain text for import into other software or computer scripts. The user chooses the molecular chain (which sequence) and the shift list to use at the top of the popup, together with a few other options that control how things are rendered. Then buttons are toggled to select which kinds of atom will be displayed in aligned columns; other kinds will simply be listed to the right of the columns. Thus for example if the shift list does not contain any carbonyl resonances in a protein chain then the user may toggle the empty "C" column off. Once the desired layout is achieved the user then uses the [Export PostScript] or [Export Text] buttons to write the data into a file of the appropriate type. The user will be presented wit ha file browser to specify the location and the name of the file to be saved. It should be noted that although the graphical display in the popup itself is somewhat limited, e.g. the gaps and spacing doesn't always look perfect, the PostScript version that is exported is significantly neater. **Caveats & Tips** If you need a chemical shift list represented in a particular format, specific for a particular external NMR program then you should use the FormatConverter software. Chemical shifts may also be exported from any table in Analysis that contains such data by clicking the right mouse button over the table and selecting the export option. """ def __init__(self, parent, *args, **kw): self.shiftList = None self.font = 'Helvetica 10' self.boldFont = 'Helvetica 10 bold' self.symbolFont = 'Symbol 8' self.smallFont = 'Helvetica 8' self.chain = None self.textOut = '' self.textMatrix = [] BasePopup.__init__(self, parent=parent, title='Chart : Chemical Shifts Table') def body(self, guiFrame): row = 0 frame = Frame(guiFrame, grid=(row, 0)) frame.expandGrid(None, 6) label = Label(frame, text='Chain:', grid=(0, 0)) tipText = 'Selects which molecular chain to show residues and chemical shift values for' self.chainPulldown = PulldownList(frame, callback=self.changeChain, grid=(0, 1), tipText=tipText) label = Label(frame, text=' Shift List:', grid=(0, 2)) tipText = 'Selects which shift list is used to derive the displayed chemical shift values' self.shiftListPulldown = PulldownList(frame, callback=self.changeShiftList, grid=(0, 3), tipText=tipText) label = Label(frame, text=' List all shifts:', grid=(0, 4)) tipText = 'Sets whether to display all the chemical shifts for residues or just for the nominated atom types in columns' self.otherShiftsSelect = CheckButton(frame, callback=self.draw, grid=(0, 5), tipText=tipText) utilButtons = UtilityButtonList(frame, helpUrl=self.help_url, grid=(0, 7)) row += 1 frame = Frame(guiFrame, grid=(row, 0)) frame.expandGrid(None, 6) label = Label(frame, text=' 1-letter codes:', grid=(0, 0)) tipText = 'Whether to use 1-letter residue codes in the table, or otherwise Ccp/three-letter codes' self.oneLetterSelect = CheckButton(frame, callback=self.draw, grid=(0, 1), selected=False, tipText=tipText) precisions = [0.1, 0.01, 0.001] texts = [str(t) for t in precisions] label = Label(frame, text=' 1H precision:', grid=(0, 2)) tipText = 'Specifies how many decimal places to use when displaying 1H chemical shift values' self.protonPrecisionSelect = PulldownList(frame, texts=texts, objects=precisions, callback=self.draw, index=1, grid=(0, 3), tipText=tipText) label = Label(frame, text=' Other precision:') label.grid(row=0, column=4, sticky='w') tipText = 'Specifies how many decimal places to use when displaying chemical shift values for isotopes other than 1H' self.otherPrecisionSelect = PulldownList(frame, texts=texts, objects=precisions, callback=self.draw, index=1, grid=(0, 5), tipText=tipText) row += 1 frame = Frame(guiFrame, grid=(row, 0)) frame.expandGrid(None, 1) label = Label(frame, text='Column\nAtoms:', grid=(0, 0)) tipText = 'Selects which kinds of atoms are displayed in aligned columns, or otherwise displayed at the end of the residue row (if "List all shifts" is set)' self.optSelector = PartitionedSelector(frame, self.toggleOpt, tipText=tipText, maxRowObjects=10, grid=(0, 1), sticky='ew') options = ['H', 'N', 'C', 'CA', 'CB', 'CG'] self.optSelector.update(objects=options, labels=options, selected=['H', 'N', 'CA']) row += 1 guiFrame.expandGrid(row, 0) self.canvasFrame = ScrolledCanvas(guiFrame, relief='groove', width=650, borderwidth=2, resizeCallback=None, grid=(row, 0), padx=1, pady=1) self.canvas = self.canvasFrame.canvas #self.canvas.bind('<Button-1>', self.toggleResidue) row += 1 tipTexts = [ 'Output information from the table as PostScript file, for printing etc.', 'Output information from the table as a whitespace separated plain text file' ] commands = [self.makePostScript, self.exportText] texts = ['Export PostScript', 'Export Text'] buttonList = ButtonList(guiFrame, commands=commands, texts=texts, grid=(row, 0), tipTexts=tipTexts) chains = self.getChains() if len(chains) > 1: self.chain = chains[1] else: self.chain = None self.updateShiftLists() self.updateChains() self.otherShiftsSelect.set(True) self.update() for func in ('__init__', 'delete'): self.registerNotify(self.updateChains, 'ccp.molecule.MolSystem.Chain', func) for func in ('__init__', 'delete'): self.registerNotify(self.updateShiftLists, 'ccp.nmr.Nmr.ShiftList', func) def changeShiftList(self, shiftList): if shiftList is not self.shiftList: self.shiftList = shiftList self.update() def updateShiftLists(self, *opt): names = [] index = 0 shiftLists = getShiftLists(self.nmrProject) shiftList = self.shiftList if shiftLists: names = [ '%s [%d]' % (sl.name or '<No name>', sl.serial) for sl in shiftLists ] if shiftList not in shiftLists: shiftList = shiftLists[0] index = shiftLists.index(shiftList) if self.shiftList is not shiftList: self.shiftList = shiftList self.shiftListPulldown.setup(names, shiftLists, index) def getChains(self): chains = [ None, ] for molSystem in self.project.sortedMolSystems(): for chain in molSystem.sortedChains(): if len(chain.residues) > 1: chains.append(chain) return chains def changeChain(self, chain): if chain is not self.chain: self.chain = chain self.update() def updateChains(self, *opt): names = [] index = -1 chains = self.getChains() chain = self.chain if len(chains) > 1: names = [ 'None', ] + ['%s:%s' % (ch.molSystem.code, ch.code) for ch in chains[1:]] if chain not in chains: chain = chains[1] index = chains.index(chain) else: chain = None self.chainPulldown.setup(names, chains, index) def destroy(self): for func in ('__init__', 'delete'): self.unregisterNotify(self.updateChains, 'ccp.molecule.MolSystem.Chain', func) for func in ('__init__', 'delete'): self.unregisterNotify(self.updateShiftLists, 'ccp.nmr.Nmr.ShiftList', func) BasePopup.destroy(self) def exportText(self, *event): from memops.gui.FileSelect import FileType from memops.gui.FileSelectPopup import FileSelectPopup if self.textOut: fileTypes = [ FileType('Text', ['*.txt']), FileType('CSV', ['*.csv']), FileType('All', ['*']) ] fileSelectPopup = FileSelectPopup(self, file_types=fileTypes, title='Save table as text', dismiss_text='Cancel', selected_file_must_exist=False) fileName = fileSelectPopup.getFile() if fileName: file = open(fileName, 'w') if fileName.endswith('.csv'): for textRow in self.textMatrix: file.write(','.join(textRow) + '\n') else: file.write(self.textOut) def toggleOpt(self, selected): self.draw() def makePostScript(self): self.canvasFrame.printCanvas() def update(self): colors = [] selected = set() atomNames = set() if self.chain: for residue in self.chain.sortedResidues(): for atom in residue.atoms: chemAtom = atom.chemAtom if colorDict.get(chemAtom.elementSymbol) is None: continue if chemAtom.waterExchangeable: continue atomNames.add(atom.name[:2]) molType = residue.molResidue.molType if molType == 'protein': selected.add('H') selected.add('N') selected.add('CA') selected.add('C') elif molType == 'DNA': selected.add("C1'") elif molType == 'RNA': selected.add("C1'") elif molType == 'carbohydrate': selected.add("C1") else: for spinSystem in self.shiftList.nmrProject.resonanceGroups: if not spinSystem.residue: for resonance in spinSystem.resonances: for name in resonance.assignNames: atomNames.add(name) options = list(atomNames) molType = 'protein' if self.chain: molType = self.chain.molecule.molType options = greekSortAtomNames(options, molType=molType) if 'H' in options: options.remove('H') options = [ 'H', ] + options colors = [colorDict.get(n[0]) for n in options] if options and not selected: selected = [ options[0], ] self.optSelector.update(objects=options, labels=options, colors=colors, selected=list(selected)) self.draw() def draw(self, *opt): if not self.shiftList: return nmrProject = self.shiftList.nmrProject shiftList = self.shiftList font = self.font bfont = self.boldFont symbolFont = self.symbolFont sFont = self.smallFont bbox = self.canvas.bbox doOthers = self.otherShiftsSelect.get() spc = 4 gap = 14 x = gap y = gap ct = self.canvas.create_text cl = self.canvas.create_line cc = self.canvas.coords self.canvas.delete('all') ssDict = {} formatDict = { 0.1: '%.1f', 0.01: '%.2f', 0.001: '%.3f', } protonFormat = formatDict[self.protonPrecisionSelect.getObject()] otherFormat = formatDict[self.otherPrecisionSelect.getObject()] uSpinSystems = [] chains = set() molSystems = set() for spinSystem in nmrProject.resonanceGroups: residue = spinSystem.residue if residue: ssDict[residue] = ssDict.get(residue, []) + [ spinSystem, ] else: uSpinSystems.append((spinSystem.serial, spinSystem)) uSpinSystems.sort() commonAtoms = self.optSelector.getSelected() N = len(commonAtoms) chain = self.chain if chain: spinSystems = [] for residue in chain.sortedResidues(): spinSystems0 = ssDict.get(residue, []) for spinSystem in spinSystems0: if spinSystem and spinSystem.resonances: spinSystems.append([residue, spinSystem]) else: spinSystems = uSpinSystems strings = [] doOneLetter = self.oneLetterSelect.get() if spinSystems: x = gap y += gap numItems = [] codeItems = [] commonItems = [] otherItems = [] numWidth = 0 codeWidth = 0 commonWidths = [0] * N commonCounts = [0] * N for residue, spinSystem in spinSystems: if type(residue) is type(1): seqNum = '{%d}' % residue if doOneLetter: ccpCode = '-' else: ccpCode = spinSystem.ccpCode or '' else: if doOneLetter: ccpCode = residue.chemCompVar.chemComp.code1Letter else: ccpCode = getResidueCode(residue) seqNum = str(residue.seqCode) subStrings = [] subStrings.append(seqNum) subStrings.append(ccpCode) item = ct(x, y, text=seqNum, font=font, anchor='se') box = bbox(item) iWidth = box[2] - box[0] numWidth = max(numWidth, iWidth) numItems.append(item) item = ct(x, y, text=ccpCode, font=font, anchor='sw') box = bbox(item) iWidth = box[2] - box[0] codeWidth = max(codeWidth, iWidth) codeItems.append(item) commonShifts, commonElements, otherShifts = self.getShiftData( spinSystem, shiftList, commonAtoms) items = [] for i in range(N): values = commonShifts[i] element = commonElements[i] if element == 'H': shiftFormat = protonFormat else: shiftFormat = otherFormat subItems = [] for value in values: text = shiftFormat % value if text: item = ct(x, y, text=text, font=font, anchor='se') box = bbox(item) iWidth = box[2] - box[0] commonWidths[i] = max(commonWidths[i], iWidth) commonCounts[i] += 1 subItems.append(item) subStrings.append( ','.join([shiftFormat % v for v in values]) or '-') items.append(subItems) commonItems.append(items) if doOthers: items0 = [] i = 0 I = len(otherShifts) for atomLabel, element, value in otherShifts: label = atomLabel if label[0] == '?': label = label[3:-1] if element == 'H': shiftFormat = protonFormat else: shiftFormat = otherFormat subStrings.append('%6s:%-4s' % (shiftFormat % value, label)) i += 1 atoms = atomLabel.split('|') items = [] j = 0 for atom in atoms: text = element if j > 0: text = '/' + text item = ct(x, y, text=text, font=font, anchor='sw') box = bbox(item) iWidth = box[2] - box[0] - 3 items.append((iWidth, item, 0)) p = len(element) if len(atom) > p: letter = atom[p] if letter not in ('0123456789'): item = ct(x, y, text=letter.lower(), font=symbolFont, anchor='sw') box = bbox(item) iWidth = box[2] - box[0] - 2 items.append((iWidth, item, -4)) p += 1 text = atom[p:] if text: item = ct(x, y, text=text, font=sFont, anchor='sw') box = bbox(item) iWidth = box[2] - box[0] - 2 items.append((iWidth, item, -4)) j += 1 text = ' ' + shiftFormat % value if i != I: text += ',' item = ct(x, y, text=text, font=font, anchor='sw') box = bbox(item) iWidth = box[2] - box[0] - 4 items.append((iWidth, item, 0)) items0.append(items) otherItems.append(items0) strings.append(subStrings) y0 = y x = x0 = gap + numWidth + codeWidth + spc + spc for i in range(N): if not commonCounts[i]: continue x += commonWidths[i] + spc + spc element = commonElements[i] iWidth = 0 text = commonAtoms[i][len(element):].lower() if text: item = ct(x, y, text=text, font=symbolFont, anchor='se') box = bbox(item) iWidth = box[2] - box[0] - 2 ct(x - iWidth, y, text=element, font=font, anchor='se') y += gap for i in range(len(numItems)): x = gap + numWidth + spc cc(numItems[i], x, y) x += spc cc(codeItems[i], x, y) x += codeWidth x1 = x + spc yM = y for j in range(N): if not commonCounts[j]: continue x += commonWidths[j] + spc + spc items = commonItems[i][j] yB = y - gap for item in items: yB += gap cc(item, x, yB) yM = max(yB, yM) x += gap if doOthers: x += spc x3 = x for items in otherItems[i]: if x > 550: x = x3 y += gap for iWidth, item, dy in items: cc(item, x, y + dy) x += iWidth y = max(y, yM) y += gap x = x0 for i in range(N): if not commonCounts[i]: continue x += commonWidths[i] + spc + spc cl(x + 8, y0, x + 8, y - gap, width=0.3, fill='#808080') cl(x1, y0, x1, y - gap, width=0.3, fill='#808080') cl(0, 0, 550, 0, width=0.3, fill='#FFFFFF') y += gap textWidths = {} for subStrings in strings: for i, text in enumerate(subStrings): if text and len(text) > textWidths.get(i, 0): textWidths[i] = len(text) else: textWidths[i] = 0 formats = {} for i in textWidths.keys(): formats[i] = ' %%%ds' % max(6, textWidths[i]) textOut = '!' textRow = ['', ''] textMatrix = [textRow] textOut += ' ' * (max(6, textWidths.get(0, 6)) + max(6, textWidths.get(1, 6)) + 1) i = 2 for atom in commonAtoms: if i in formats: textOut += formats[i] % atom textRow.append((formats[i] % atom).strip()) i += 1 textOut += '\n' for subStrings in strings: textRow = [] textMatrix.append(textRow) i = 0 for text in subStrings: textOut += formats[i] % text textRow.append((formats[i] % text).strip()) i += 1 textOut += '\n' self.textOut = textOut self.textMatrix = textMatrix def getShiftData(self, spinSystem, shiftList, commonAtoms=('H', 'N', 'CA', 'CB')): commonShifts = [] commonResonances = {} commonElements = [] for atomName in commonAtoms: elements = set() resonances = [] for resonance in spinSystem.resonances: resonanceSet = resonance.resonanceSet if resonanceSet: for atomSet in resonanceSet.atomSets: for atom in atomSet.atoms: if atomName == atom.name[:2]: resonances.append(resonance) break else: continue break else: for assignName in resonance.assignNames: if atomName == assignName[:2]: resonances.append(resonance) break shiftValues = [] for resonance in resonances: isotope = resonance.isotope if isotope: elements.add(isotope.chemElement.symbol) commonResonances[resonance] = True shift = resonance.findFirstShift(parentList=shiftList) if shift: shiftValues.append(shift.value) if not elements: element = atomName[0] else: element = elements.pop() commonElements.append(element) commonShifts.append(shiftValues) otherShifts = [] for resonance in spinSystem.resonances: if not commonResonances.get(resonance): shift = resonance.findFirstShift(parentList=shiftList) if shift: isotope = resonance.isotope if isotope: element = isotope.chemElement.symbol else: element = '??' if resonance.assignNames or resonance.resonanceSet: name = getResonanceName(resonance) else: name = '??[%d]' % resonance.serial otherShifts.append((name, element, shift.value)) molType = 'protein' if spinSystem.residue: molType = spinSystem.residue.molResidue.molType otherShifts = greekSortAtomNames(otherShifts, molType) return commonShifts, commonElements, otherShifts
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)