class ResultsTab(object): def __init__(self, parent, frame): self.guiParent = parent self.frame = frame self.project = parent.project self.nmrProject = parent.nmrProject self.selectedLinkA = None self.selectedLinkB = None self.selectedResidueA = None self.selectedResidueB = None self.selectedLink = None self.dataModel = self.guiParent.connector.results self.body() def body(self): frame = self.frame self.resultsResidueNumber = 1 frame.expandGrid(5, 0) resultTopFrame = LabelFrame(frame, text='Which results to show') resultTopFrame.grid(row=0, column=0, sticky='ew') self.resultsResidueNumber = 3 texts = [' < '] commands = [self.resultsPrevResidue] self.resultsPreviousButton = ButtonList(resultTopFrame, commands=commands, texts=texts) self.resultsPreviousButton.grid(row=0, column=1, sticky='nsew') tipText = 'The Number of the residue in the sequence to display results for' self.resultsResidueNumberEntry = IntEntry( resultTopFrame, grid=(0, 2), width=7, text=3, returnCallback=self.resultsUpdateAfterEntry, tipText=tipText) #self.resultsResidueNumberEntry.bind('<Leave>', self.resultsUpdateAfterEntry, '+') texts = [' > '] commands = [self.resultsNextResidue] self.resultsNextButton = ButtonList(resultTopFrame, commands=commands, texts=texts) self.resultsNextButton.grid(row=0, column=3, sticky='nsew') selectCcpCodes = ['residue'] + AMINO_ACIDS self.resultsSelectedCcpCode = 'residue' tipText = 'Instead of going through the sequence residue by residue, jump directly to next amino acid of a specific type.' resultsSelectCcpCodeLabel = Label( resultTopFrame, text='Directly jump to previous/next:', grid=(0, 4)) self.resultsSelectCcpCodePulldown = PulldownList( resultTopFrame, callback=self.resultsChangeSelectedCcpCode, texts=selectCcpCodes, index=selectCcpCodes.index(self.resultsSelectedCcpCode), grid=(0, 5), tipText=tipText) self.selectedSolution = 1 runLabel = Label(resultTopFrame, text='run:') runLabel.grid(row=0, column=6) texts = [' < '] commands = [self.resultsPrevSolution] self.resultsPreviousSolutionButton = ButtonList(resultTopFrame, commands=commands, texts=texts) self.resultsPreviousSolutionButton.grid(row=0, column=7, sticky='nsew') tipText = 'If you ran the algorithm more than once, you can select the solution given by the different runs.' self.resultsSolutionNumberEntry = IntEntry( resultTopFrame, grid=(0, 8), width=7, text=1, returnCallback=self.solutionUpdateAfterEntry, tipText=tipText) #self.resultsSolutionNumberEntry.bind('<Leave>', self.solutionUpdateAfterEntry, '+') texts = [' > '] commands = [self.resultsNextSolution] self.resultsNextSolutionButton = ButtonList(resultTopFrame, commands=commands, texts=texts) self.resultsNextSolutionButton.grid(row=0, column=9, sticky='nsew') self.energyLabel = Label(resultTopFrame, text='energy:') self.energyLabel.grid(row=0, column=10) texts = ['template for puzzling'] commands = [self.adoptSolution] self.adoptButton = ButtonList(resultTopFrame, commands=commands, texts=texts) self.adoptButton.grid(row=0, column=11, sticky='nsew') # LabelFrame(frame, text='Spin Systems') resultsSecondFrame = Frame(frame) resultsSecondFrame.grid(row=2, column=0, sticky='nsew') resultsSecondFrame.grid_columnconfigure(0, weight=1) resultsSecondFrame.grid_columnconfigure(1, weight=1) resultsSecondFrame.grid_columnconfigure(2, weight=1) resultsSecondFrame.grid_columnconfigure(3, weight=1) resultsSecondFrame.grid_columnconfigure(4, weight=1) headingList = ['#', '%'] tipTexts = [ 'Spinsystem number {} indicates serial of the spinsystem. If the spinsystem was already assigned to a residue, the residue number is shown aswell', 'percentage of the solutions that connected this spinsystem to this residue' ] editWidgets = [None, None, None] self.displayResultsTables = [] self.residueLabels = [] for i in range(5): label = Label(resultsSecondFrame, text='residue') label.grid(row=0, column=i) #editGetCallbacks = [createCallbackFunction(i)]*3 displayResultsTable = ScrolledMatrix( resultsSecondFrame, headingList=headingList, multiSelect=False, tipTexts=tipTexts, callback=self.selectSpinSystemForTable, passSelfToCallback=True) displayResultsTable.grid(row=2, column=i, sticky='nsew') displayResultsTable.sortDown = False self.residueLabels.append(label) self.displayResultsTables.append(displayResultsTable) # LabelFrame(frame, text='Sequence Fragment') resultsFirstFrame = Frame(resultsSecondFrame) resultsFirstFrame.grid(row=1, column=0, sticky='ew', columnspan=5) resultsFirstFrame.grid_rowconfigure(0, weight=1) resultsFirstFrame.grid_rowconfigure(1, weight=1) resultsFirstFrame.grid_columnconfigure(0, weight=1) texts = [ ' res 1 ', ' links ', ' res 2 ', ' links ', ' res 3 ', ' links ', ' res 4 ', ' links ', ' res 5 ' ] commands = [ lambda: self.selectRelativeResidue(1, True), lambda: self.selectLink(1, True), lambda: self.selectRelativeResidue(2, True), lambda: self.selectLink(2, True), lambda: self.selectRelativeResidue(3, True), lambda: self.selectLink(3, True), lambda: self.selectRelativeResidue(4, True), lambda: self.selectLink(4, True), lambda: self.selectRelativeResidue(5, True) ] self.sequenceButtons = ButtonList(resultsFirstFrame, commands=commands, texts=texts) self.sequenceButtons.grid(row=0, column=0, sticky='nsew') for n, button in enumerate(self.sequenceButtons.buttons): if n % 2: button.grid(column=n, sticky='ns') self.sequenceButtons.grid_columnconfigure(n, weight=0) else: self.sequenceButtons.grid_columnconfigure(n, uniform=2) spacer = Spacer(resultsFirstFrame) spacer.grid(row=1, column=0, sticky='nsew') texts = [ ' res 1 ', ' links ', ' res 2 ', ' links ', ' res 3 ', ' links ', ' res 4 ', ' links ', ' res 5 ' ] commands = commands = [ lambda: self.selectRelativeResidue(1, False), lambda: self.selectLink(1, False), lambda: self.selectRelativeResidue(2, False), lambda: self.selectLink(2, False), lambda: self.selectRelativeResidue(3, False), lambda: self.selectLink(3, False), lambda: self.selectRelativeResidue(4, False), lambda: self.selectLink(4, False), lambda: self.selectRelativeResidue(5, False) ] self.sequenceButtonsB = ButtonList(resultsFirstFrame, commands=commands, texts=texts) self.sequenceButtonsB.grid(row=2, column=0, sticky='nsew') for n, button in enumerate(self.sequenceButtonsB.buttons): if n % 2: button.grid(column=n, sticky='ns') self.sequenceButtonsB.grid_columnconfigure(n, weight=0) else: self.sequenceButtonsB.grid_columnconfigure(n, uniform=2) frame.grid_rowconfigure(3, weight=2) resultsThirdFrame = Frame(frame) resultsThirdFrame.grid(row=3, column=0, sticky='nsew') resultsThirdFrame.grid_rowconfigure(0, weight=1) resultsThirdFrame.grid_columnconfigure(0, weight=1) tabbedFrameB = TabbedFrame(resultsThirdFrame, options=['Peaks', 'Spin System'], callback=self.toggleTab, grid=(0, 0)) #self.tabbedFrameB = tabbedFrame PeakFrame, SpinSystemFrame = tabbedFrameB.frames SpinSystemFrame.grid_rowconfigure(0, weight=1) PeakFrame.grid_rowconfigure(1, weight=1) SpinSystemFrame.grid_columnconfigure(0, weight=1) PeakFrame.grid_columnconfigure(0, weight=1) headingList = [ 'residue', 'assigned to in project', 'user defined sequence', 'selected annealing result', '%' ] tipTexts = [None, None, None, None, None] editWidgets = [None, None, None, None, None] editGetCallbacks = [None, None, None, None, None] editSetCallbacks = [None, None, None, None, None] self.spinSysTable = ScrolledMatrix(SpinSystemFrame, headingList=headingList, editWidgets=editWidgets, multiSelect=False, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, tipTexts=tipTexts) self.spinSysTable.grid(row=0, column=0, sticky='nsew') buttonFrameinPeakFrame = Frame(PeakFrame) buttonFrameinPeakFrame.grid(sticky='ew') self.findButton = Button( buttonFrameinPeakFrame, text=' Go to Peak ', borderwidth=1, padx=2, pady=1, command=self.findPeak, tipText='Locate the currently selected peak in the specified window' ) self.findButton.grid(row=0, column=0, sticky='e') label = Label(buttonFrameinPeakFrame, text='in window:') label.grid(row=0, column=1, sticky='w') self.windowPulldown = PulldownList( buttonFrameinPeakFrame, callback=self.selectWindowPane, tipText='Choose the spectrum window for locating peaks or strips') self.windowPulldown.grid(row=0, column=2, sticky='w') self.assignSelectedPeaksButton = Button( buttonFrameinPeakFrame, text='Assign Resonances to Peak(s)', borderwidth=1, padx=2, pady=1, command=self.assignSelectedPeaks, tipText= 'Assign resonances to peak dimensions, this of course only works when the peak is found in the spectrum.' ) self.assignSelectedPeaksButton.grid(row=0, column=3, sticky='ew') self.assignSelectedSpinSystemsToResiduesButton = Button( buttonFrameinPeakFrame, text='Assign Spinsystems to Residues', borderwidth=1, padx=2, pady=1, command=self.assignSelectedSpinSystemsToResidues, tipText='Assign spinsystems to residues') self.assignSelectedSpinSystemsToResiduesButton.grid(row=0, column=4, sticky='ew') headingList = [ '#', 'spectrum', 'Dim1', 'Dim2', 'Dim3', 'c.s. dim1', 'c.s. dim2', 'c.s. dim3', 'colabelling' ] tipTexts = [ 'Peak number, only present when the peak was actually found in the spectrum.', 'Name of the spectrum', 'Name of atomSet measured in this dimension. Dimension number corresponds to Ref Exp Dim as indicated by going in the main menu to Experiment-->Experiments-->Experiment Type', 'Name of atomSet measured in this dimension. Dimension number corresponds to Ref Exp Dim as indicated by going in the main menu to Experiment-->Experiments-->Experiment Type', 'Name of atomSet measured in this dimension. Dimension number corresponds to Ref Exp Dim as indicated by going in the main menu to Experiment-->Experiments-->Experiment Type', 'Chemical Shift', 'Chemical Shift', 'Chemical Shift', 'Colabbeling fraction over all nuclei that are on the magnetization transfer pathway during the experiment that gave rise to the peak, including visited nuclei that were not measured in any of the peak dimensions' ] #editWidgets = [None, None, None, None, None, None, None, None, None] editGetCallbacks = [ None, None, None, None, None, None, None, None, None ] #editGetCallbacks = [self.selectPeak, self.selectPeak, self.selectPeak, self.selectPeak, self.selectPeak, self.selectPeak, self.selectPeak, self.selectPeak, self.selectPeak] editSetCallbacks = [ None, None, None, None, None, None, None, None, None ] self.displayPeakTable = ScrolledMatrix(PeakFrame, headingList=headingList, multiSelect=True, tipTexts=tipTexts) #editWidgets=editWidgets, multiSelect=True, # editGetCallbacks=editGetCallbacks, # editSetCallbacks=editSetCallbacks, # tipTexts=tipTexts) self.displayPeakTable.grid(row=1, column=0, sticky='nsew') self.windowPane = None self.updateWindows() def selectSpinSystemForTable(self, spinSystem, row, column, table): table_number = self.displayResultsTables.index(table) self.selectSpinSystem(table_number, spinSystem) @lockUntillResults def showResults(self): self.updateResultsTable() @lockUntillResults def selectLink(self, number, topRow): if topRow: self.selectedResidueA = None self.selectedResidueB = None self.selectedLinkA = number self.selectedLinkB = None else: self.selectedResidueA = None self.selectedResidueB = None self.selectedLinkA = None self.selectedLinkB = number self.updateButtons() self.updateLink() @lockUntillResults def selectRelativeResidue(self, number, topRow): if topRow: self.selectedResidueA = number self.selectedResidueB = None self.selectedLinkA = None self.selectedLinkB = None else: self.selectedResidueA = None self.selectedResidueB = number self.selectedLinkA = None self.selectedLinkB = None self.updateButtons() self.updateLink() def updateLink(self): ''' Checks for any selected link (self.selectedLinkA or self.selectedLinkB) and calls updatePeakTable with the correct residue Object and spinsystem Objects. ''' number = self.selectedLinkA or self.selectedLinkB or self.selectedResidueA or self.selectedResidueB if not number: self.emptyPeakTable() return dataModel = self.dataModel resNumber = self.resultsResidueNumber chain = dataModel.chain residues = chain.residues solutionNumber = self.selectedSolution - 1 if self.selectedResidueA: res = residues[resNumber - 4 + number] spinSystem = res.solutions[solutionNumber] self.selectedLink = None if res and spinSystem: self.selectedLink = res.getIntraLink(spinSystem) #self.updatePeakTableIntra(res, spinSystem) self.updateSpinSystemTable(spinSystem) elif self.selectedResidueB: res = residues[resNumber - 4 + number] spinSystem = res.userDefinedSolution self.selectedLink = None if res and spinSystem: self.selectedLink = res.getIntraLink(spinSystem) #self.updatePeakTableIntra(res, spinSystem) self.updateSpinSystemTable(spinSystem) elif self.selectedLinkA: resA = residues[resNumber - 4 + number] resB = residues[resNumber - 3 + number] spinSystemA = resA.solutions[solutionNumber] spinSystemB = resB.solutions[solutionNumber] self.selectedLink = None if resA and spinSystemA and spinSystemB: self.selectedLink = resA.getLink(spinSystemA, spinSystemB) #self.updatePeakTable(resA, spinSystemA, spinSystemB) # and resA.userDefinedSolution and resB.userDefinedSolution: elif self.selectedLinkB: resA = residues[resNumber - 4 + number] resB = residues[resNumber - 3 + number] spinSystemA = resA.userDefinedSolution spinSystemB = resB.userDefinedSolution self.selectedLink = None if resA and spinSystemA and spinSystemB: self.selectedLink = resA.getLink(spinSystemA, spinSystemB) #self.updatePeakTable(resA, spinSystemA, spinSystemB) self.updatePeakTable() def emptyPeakTable(self): self.displayPeakTable.update(objectList=[], textMatrix=[], colorMatrix=[]) def updatePeakTable(self): ''' Updates the peak table to show the peaks that are found for a sequencial pair of spinsystems A and B. If there is not a linkobject found for spinsystems A and B the table is emptied. Also sets the selected peak to None. ''' link = self.selectedLink if not link: self.emptyPeakTable() else: resA, resB = link.getResidues() spinSystemA, spinSystemB = link.getSpinSystems() data = [] objectList = [] peakLinks = link.getAllPeakLinks() if not peakLinks: self.emptyPeakTable() return maxDimenionality = max([ len(peakLink.getSimulatedPeak().getContribs()) for peakLink in peakLinks ]) for peakLink in peakLinks: serial = None realPeak = peakLink.getPeak() simPeak = peakLink.getSimulatedPeak() atomTexts = [None] * maxDimenionality chemicalShifts = [None] * maxDimenionality for simulatedPeakContrib in simPeak.getContribs(): atomName = simulatedPeakContrib.getAtomName() #ccpCode = simulatedPeakContrib.getCcpCode() dimNumber = simulatedPeakContrib.getDimNumber() if resA is simulatedPeakContrib.getResidue(): spinSystemDescription = spinSystemA.getDescription( noSerialWhenSeqCodeIsPresent=True) else: spinSystemDescription = spinSystemB.getDescription( noSerialWhenSeqCodeIsPresent=True) atomTexts[dimNumber - 1] = '%s %s' % (spinSystemDescription, atomName) if realPeak: serial = realPeak.getSerial() for dim in realPeak.getDimensions(): chemicalShifts[dim.getDimNumber() - 1] = dim.getChemicalShift() else: shiftListSerial = simPeak.getSpectrum().getShiftListSerial( ) for resonance, simulatedPeakContrib in zip( peakLink.getResonances(), simPeak.getContribs()): if resonance: chemicalShifts[simulatedPeakContrib.getDimNumber() - 1] = resonance.getChemicalShift( shiftListSerial) else: chemicalShifts[simulatedPeakContrib.getDimNumber() - 1] = '?' data.append([serial, simPeak.getSpectrum().name] + atomTexts + chemicalShifts + [simPeak.colabelling]) objectList.append(peakLink) headingList = ['#', 'spectrum'] + [ 'dim%s' % a for a in range(1, maxDimenionality + 1) ] + ['c.s. dim%s' % a for a in range(1, maxDimenionality + 1)] + ['colabbeling'] self.displayPeakTable.update(objectList=objectList, textMatrix=data, headingList=headingList) def findPeak(self): if not self.windowPane: return selectedPeakLinks = self.displayPeakTable.currentObjects if not selectedPeakLinks: self.guiParent.updateInfoText('Please select a peak first.') return if len(selectedPeakLinks) > 1: self.guiParent.updateInfoText('Can only go to one peak at a time.') return selectedPeakLink = selectedPeakLinks[0] selectedPeak = selectedPeakLink.getPeak() if selectedPeak: ccpnPeak = selectedPeak.getCcpnPeak() createPeakMark(ccpnPeak, lineWidth=2.0) windowFrame = self.windowPane.getWindowFrame() windowFrame.gotoPeak(ccpnPeak) else: simPeak = selectedPeakLink.getSimulatedPeak() spectrum = simPeak.getSpectrum() ccpnSpectrum = spectrum.getCcpnSpectrum() view = getSpectrumWindowView(self.windowPane, ccpnSpectrum) if not view: self.guiParent.updateInfoText( 'This peak cannot be displayed in the window you chose.') axisMappingByRefExpDimNumber = {} for axisMapping in view.axisMappings: refExpDimNumber = axisMapping.analysisDataDim.dataDim.expDim.refExpDim.dim axisMappingByRefExpDimNumber[refExpDimNumber] = axisMapping positionToGoTo = {} markPosition = [] axisTypes = [] for resonance, contrib in zip(selectedPeakLink.getResonances(), simPeak.getContribs()): dimNumber = contrib.getDimNumber() axisMapping = axisMappingByRefExpDimNumber.get(dimNumber) label = axisMapping.label if resonance: axisType = axisMapping.axisPanel.axisType chemicalShift = resonance.getChemicalShift() positionToGoTo[label] = chemicalShift markPosition.append(chemicalShift) axisTypes.append(axisType) # Not drawing a mark at this chemical shift, just hoovering to # the good region in the spectrum else: ccpCode = contrib.getResidue().getCcpCode() atomName = contrib.getAtomName() medianChemicalShift = self.getMedianChemicalShift( ccpCode, atomName) if medianChemicalShift: positionToGoTo[label] = medianChemicalShift if positionToGoTo: windowFrame = self.windowPane.getWindowFrame() windowFrame.gotoPosition(positionToGoTo) if markPosition: createNonPeakMark(markPosition, axisTypes) def assignSelectedPeaks(self): selectedPeakLinks = self.displayPeakTable.currentObjects for pl in selectedPeakLinks: peak = pl.getPeak() if peak: for resonance, dimension in zip(pl.getResonances(), peak.getDimensions()): ccpnResonance = resonance.getCcpnResonance() ccpnDimension = dimension.getCcpnDimension() if ccpnResonance and ccpnDimension: assignResToDim(ccpnDimension, ccpnResonance) def assignSelectedSpinSystemsToResidues(self): link = self.selectedLink if link: residues = link.getResidues() spinSystems = link.getSpinSystems() ccpnSpinSystems = [] ccpnResidues = [] for spinSys, res in zip(spinSystems, residues): if spinSys and res: ccpnSpinSystems.append(spinSys.getCcpnResonanceGroup()) ccpnResidues.append(res.getCcpnResidue()) assignSpinSystemstoResidues(ccpnSpinSystems, ccpnResidues, guiParent=self.guiParent) self.updateButtons() self.updateButtons() self.updateResultsTable() self.updatePeakTable() def getMedianChemicalShift(self, ccpCode, atomName): nmrRefStore = self.project.findFirstNmrReferenceStore( molType='protein', ccpCode=ccpCode) chemCompNmrRef = nmrRefStore.findFirstChemCompNmrRef( sourceName='RefDB') chemCompVarNmrRef = chemCompNmrRef.findFirstChemCompVarNmrRef( linking='any', descriptor='any') if chemCompVarNmrRef: chemAtomNmrRef = chemCompVarNmrRef.findFirstChemAtomNmrRef( name=atomName) if chemAtomNmrRef: distribution = chemAtomNmrRef.distribution maxIndex = max([ (value, index) for index, value in enumerate(distribution) ])[1] return chemAtomNmrRef.refValue + chemAtomNmrRef.valuePerPoint * ( maxIndex - chemAtomNmrRef.refPoint) return None def selectWindowPane(self, windowPane): if windowPane is not self.windowPane: self.windowPane = windowPane def updateWindows(self): index = 0 windowPane = None windowPanes = [] names = [] peakList = None tryWindows = WindowBasic.getActiveWindows(self.project) windowData = [] getName = WindowBasic.getWindowPaneName for window in tryWindows: for windowPane0 in window.spectrumWindowPanes: # if WindowBasic.isSpectrumInWindowPane(windowPane0, spectrum): windowData.append((getName(windowPane0), windowPane0)) windowData.sort() names = [x[0] for x in windowData] windowPanes = [x[1] for x in windowData] if windowPanes: if windowPane not in windowPanes: windowPane = windowPanes[0] index = windowPanes.index(windowPane) else: windowPane = None self.selectWindowPane(windowPane) self.windowPulldown.setup(names, windowPanes, index) def selectSpinSystem(self, number, spinSystem): res = self.dataModel.chain.residues[self.resultsResidueNumber - 3 + number] oldSpinSystemForResidue = res.userDefinedSolution if oldSpinSystemForResidue and res.getSeqCode( ) in oldSpinSystemForResidue.userDefinedSolutions: oldSpinSystemForResidue.userDefinedSolutions.remove( res.getSeqCode()) res.userDefinedSolution = spinSystem spinSystem.userDefinedSolutions.append(res.getSeqCode()) # self.updateSpinSystemTable(spinSystem) self.updateLink() self.updateButtons() def updateSpinSystemTable(self, spinSystem): if not spinSystem: self.emptySpinSystemTable() return dataModel = self.dataModel residues = dataModel.chain.residues data = [] colorMatrix = [] for residue in spinSystem.allowedResidues: oneRow = [] oneRowColor = [] string = str(residue.getSeqCode()) + ' ' + residue.getCcpCode() oneRow.append(string) resonanceGroup = spinSystem.getCcpnResonanceGroup() ccpnResidue = residue.ccpnResidue # Assigned in the project to this residue if resonanceGroup and resonanceGroup.residue and resonanceGroup.residue is ccpnResidue: oneRow.append('x') else: oneRow.append(None) # The user selected this res for this spinsystem (could be more # than one res for which this happens) if residue.getSeqCode() in spinSystem.userDefinedSolutions: oneRow.append('x') else: oneRow.append(None) if residue.solutions[self.selectedSolution - 1] == spinSystem: oneRow.append('x') else: oneRow.append(None) if spinSystem.solutions: percentage = spinSystem.solutions.count( residue.getSeqCode()) / float(len( spinSystem.solutions)) * 100.0 else: percentage = 0 oneRow.append(int(percentage + 0.5)) color = pick_color_by_percentage(percentage) oneRowColor = [color] * 5 data.append(oneRow) colorMatrix.append(oneRowColor) self.spinSysTable.update(objectList=data, textMatrix=data, colorMatrix=colorMatrix) self.spinSysTable.sortDown = False self.spinSysTable.sortLine(-1, noUpdate=True) def emptySpinSystemTable(self): self.spinSysTable.update(objectList=[], textMatrix=[], colorMatrix=[]) @lockUntillResults def adoptSolution(self): dataModel = self.dataModel selectedSolution = self.selectedSolution for res in dataModel.chain.residues: spinSystem = res.solutions[selectedSolution - 1] res.userDefinedSolution = spinSystem spinSystem.userDefinedSolutions = [res.getSeqCode()] self.updateLink() self.updateButtons() @lockUntillResults def resultsPrevSolution(self): if self.selectedSolution != 1: self.selectedSolution = self.selectedSolution - 1 self.resultsSolutionNumberEntry.set(self.selectedSolution) self.updateLink() self.updateButtons() self.updateEnergy() @lockUntillResults def resultsNextSolution(self): amountOfRepeats = len(self.dataModel.chain.residues[0].solutions) if self.selectedSolution < amountOfRepeats: self.selectedSolution = self.selectedSolution + 1 self.resultsSolutionNumberEntry.set(self.selectedSolution) self.updateLink() self.updateButtons() self.updateEnergy() @lockUntillResults def resultsPrevResidue(self): residues = self.dataModel.chain.residues #chainLength = len(residues) new_value = self.resultsResidueNumber if self.resultsSelectedCcpCode == 'residue': if self.resultsResidueNumber != 3: new_value = self.resultsResidueNumber - 1 else: for res in residues: if res.getSeqCode() == self.resultsResidueNumber: break elif res.getCcpCode() == self.resultsSelectedCcpCode: new_value = res.getSeqCode() if new_value < 3: new_value = 3 if self.resultsResidueNumber != new_value: self.resultsResidueNumber = new_value self.resultsResidueNumberEntry.set(self.resultsResidueNumber) self.updateLink() self.updateButtons() self.updateButtons() self.updateResultsTable() self.updateResidueLabels() @lockUntillResults def resultsNextResidue(self): residues = self.dataModel.chain.residues chainLength = len(residues) new_value = self.resultsResidueNumber if self.resultsSelectedCcpCode == 'residue': if self.resultsResidueNumber != chainLength - 2: new_value = self.resultsResidueNumber + 1 else: for res in residues[(self.resultsResidueNumber):]: if res.getCcpCode() == self.resultsSelectedCcpCode: new_value = res.getSeqCode() if new_value > chainLength - 2: new_value = chainLength - 2 break if self.resultsResidueNumber != new_value: self.resultsResidueNumber = new_value self.resultsResidueNumberEntry.set(self.resultsResidueNumber) self.updateLink() self.updateButtons() self.updateButtons() self.updateResultsTable() self.updateResidueLabels() def resultsChangeSelectedCcpCode(self, ccpCode): self.resultsSelectedCcpCode = ccpCode @lockUntillResults def resultsUpdateAfterEntry(self, event=None): ''' Update for entry of residue number in strip plots ''' residues = self.dataModel.chain.residues value = self.resultsResidueNumberEntry.get() if value == self.resultsResidueNumber: return else: self.resultsResidueNumber = value if value < 3: self.resultsResidueNumberEntry.set(3) self.resultsResidueNumber = 3 elif value > len(residues) - 2: self.resultsResidueNumber = len(residues) - 2 self.resultsResidueNumberEntry.set(self.resultsResidueNumber) else: self.resultsResidueNumberEntry.set(self.resultsResidueNumber) self.updateLink() self.updateButtons() self.updateButtons() self.updateResultsTable() self.updateResidueLabels() @lockUntillResults def solutionUpdateAfterEntry(self, event=None): ''' Update for entry of residue number in strip plots ''' Nsolutions = len(self.dataModel.chain.residues[0].solutions) value = self.resultsSolutionNumberEntry.get() if value == self.selectedSolution: return else: self.selectedSolution = value if value < 1: self.resultsSolutionNumberEntry.set(1) self.selectedSolution = 1 elif value > Nsolutions: self.selectedSolution = Nsolutions self.resultsSolutionNumberEntry.set(self.selectedSolution) else: self.resultsSolutionNumberEntry.set(self.selectedSolution) self.updateLink() self.updateButtons() def update(self): self.updateLink() self.updateResidueLabels() self.updateResultsTable() self.updateButtons() self.updateButtons() self.updateEnergy() def updateResultsTable(self): resNumber = self.resultsResidueNumber dataModel = self.dataModel chain = dataModel.chain residues = chain.residues resA = residues[resNumber - 3] resB = residues[resNumber - 2] resC = residues[resNumber - 1] resD = residues[resNumber] resE = residues[resNumber + 1] resList = [resA, resB, resC, resD, resE] tableList = self.displayResultsTables for res, table in zip(resList, tableList): ccpCode = res.ccpCode spinSystemsWithThisCcpCode = dataModel.getSpinSystems()[ccpCode] data = [] colorMatrix = [] objectList = [] jokers = [] realSpinSystems = [] for spinSys in spinSystemsWithThisCcpCode: if spinSys.getIsJoker(): jokers.append(spinSys) else: realSpinSystems.append(spinSys) for spinsys in realSpinSystems: oneRow = [] oneRowColor = [] # self.getStringDescriptionOfSpinSystem(spinsys) spinSystemInfo = spinsys.getDescription() oneRow.append(spinSystemInfo) assignmentPercentage = int( float(res.solutions.count(spinsys)) / len(res.solutions) * 100.0) oneRow.append(assignmentPercentage) objectList.append(spinsys) color = pick_color_by_percentage(assignmentPercentage) oneRowColor = [color, color] data.append(oneRow) colorMatrix.append(oneRowColor) if jokers: oneRow = ['Joker'] NumberOfAssignmentsToJoker = 0 for spinSys in jokers: NumberOfAssignmentsToJoker += res.solutions.count(spinSys) assignmentPercentage = int( float(NumberOfAssignmentsToJoker) / len(res.solutions) * 100.0) oneRow.append(assignmentPercentage) color = pick_color_by_percentage(assignmentPercentage) oneRowColor = [color, color] data.append(oneRow) colorMatrix.append(oneRowColor) objectList.append(jokers[0]) percentages = [datapoint[1] for datapoint in data] tableData = sorted(zip(percentages, data, objectList, colorMatrix), reverse=True) percentage, data, objectList, colorMatrix = zip(*tableData) table.update(objectList=objectList, textMatrix=data, colorMatrix=colorMatrix) def updateResidueLabels(self): resList = self.getCurrentlyDisplayedResidues() labels = self.residueLabels for residue, label in zip(resList, labels): text = str(residue.getSeqCode()) + ' ' + residue.getCcpCode() label.set(text) def updateButtons(self): self.updateButtonHighLights() self.updateResultsTopRowButtons() self.updateResultsBottomRowButtons() def updateResultsTopRowButtons(self): resList = self.getCurrentlyDisplayedResidues() buttons = self.sequenceButtons.buttons[::2] for button, res in zip(buttons, resList): spinsys = res.solutions[self.selectedSolution - 1] # str(res.getSeqCode()) + ' ' + res.getCcpCode() + ': ' + # spinsys.getDescription() # self.getStringDescriptionOfSpinSystem(spinsys) text = spinsys.getDescription(noSerialWhenSeqCodeIsPresent=False) button.config(text=text) def updateResultsBottomRowButtons(self): resList = self.getCurrentlyDisplayedResidues() buttons = self.sequenceButtonsB.buttons[::2] for button, res in zip(buttons, resList): if res.userDefinedSolution: selectedSpinSystem = res.userDefinedSolution text = selectedSpinSystem.getDescription( noSerialWhenSeqCodeIsPresent=False) if len(selectedSpinSystem.userDefinedSolutions) > 1: # The red color signals that the spinssystem is used in # more than 1 place in the sequence button.config(text=text, bg=highLightRed) else: button.config(text=text) else: # str(res.getSeqCode()) + ' ' + res.getCcpCode() + ': -' text = '-' button.config(text=text) def updateButtonHighLights(self): self.setAllButtonsToGrey() if self.selectedResidueA: buttons = [ self.sequenceButtons.buttons[0], self.sequenceButtons.buttons[2], self.sequenceButtons.buttons[4], self.sequenceButtons.buttons[6], self.sequenceButtons.buttons[8] ] buttons[self.selectedResidueA - 1].config(bg=highLightYellow) elif self.selectedResidueB: buttons = [ self.sequenceButtonsB.buttons[0], self.sequenceButtonsB.buttons[2], self.sequenceButtonsB.buttons[4], self.sequenceButtonsB.buttons[6], self.sequenceButtonsB.buttons[8] ] buttons[self.selectedResidueB - 1].config(bg=highLightYellow) elif self.selectedLinkA: buttons = [ self.sequenceButtons.buttons[1], self.sequenceButtons.buttons[3], self.sequenceButtons.buttons[5], self.sequenceButtons.buttons[7] ] buttons[self.selectedLinkA - 1].config(bg=highLightYellow) elif self.selectedLinkB: buttons = [ self.sequenceButtonsB.buttons[1], self.sequenceButtonsB.buttons[3], self.sequenceButtonsB.buttons[5], self.sequenceButtonsB.buttons[7] ] buttons[self.selectedLinkB - 1].config(bg=highLightYellow) def updateEnergy(self): text = 'energy: %s' % int( self.dataModel.getEnergy(self.selectedSolution - 1) + 0.5) self.energyLabel.set(text) self.energyLabel.update() def setAllButtonsToGrey(self): for button in self.sequenceButtons.buttons + self.sequenceButtonsB.buttons: button.config(bg='grey83') def setAllRedButtonsToGrey(self): for button in self.sequenceButtons.buttons + self.sequenceButtonsB.buttons: if button.enableFg == highLightRed: button.config(bg='grey83') def getCurrentlyDisplayedResidues(self): resNumber = self.resultsResidueNumber residues = self.dataModel.chain.residues[resNumber - 3:resNumber + 2] return residues def toggleTab(self, index): pass
class SequenceShiftPredictPopup(BasePopup): """ **Predict Protein Shifts from Sequence** This popup window is designed to allow the prediction of chemical shifts for a protein chain from the sequence (so with no structural information), using the (external) program CamCoil. The Options to select are the Chain for which the prediction is made, and the prediction type and the pH used for the prediction, and also the Shift List, which is not used for the prediction but is used for the comparison with the prediction. CamCoil has two variations, one for the prediction of random coil chemical shifts and one for prediction of protein loops chemical shifts. The Chemical Shift Predictions table lists the atoms in the chain. For each atom the data listed is the residue number, residue type, atom name, first shift found for that atom in the chosen shiftList, chemical shift predicted by CamCoil, and the difference between the actual shift and the predicted shift (if both exist). To run the prediction click on the "Run CamCoil Prediction!" button. This does not store any predicted shifts in the project. **Caveats & Tips** **References** The CamCoil programme: http://www-vendruscolo.ch.cam.ac.uk/camcoil.php *A. De Simone, A. Cavalli, S-T. D. Hsu, W. Vranken and M. Vendruscolo Accurate random coil chemical shifts from an analysis of loop regions in native states of proteins. J. Am. Chem. Soc. 131(45):16332-3 """ def __init__(self, parent, *args, **kw): self.chain = None self.shiftList = None self.predictionDict = {} BasePopup.__init__( self, parent=parent, title='Data Analysis : Predict Shifts from Sequence') def body(self, guiFrame): self.geometry('700x500') guiFrame.expandGrid(1, 0) row = 0 # TOP LEFT FRAME frame = LabelFrame(guiFrame, text='Options') frame.grid(row=row, column=0, sticky='nsew') frame.columnconfigure(7, weight=1) label = Label(frame, text='Chain') label.grid(row=0, column=0, sticky='w') self.chainPulldown = PulldownList( frame, callback=self.changeChain, tipText='Choose the molecular system chain to make predictions for' ) self.chainPulldown.grid(row=0, column=1, sticky='w') label = Label(frame, text='Shift List') label.grid(row=0, column=2, sticky='w') self.shiftListPulldown = PulldownList( frame, callback=self.changeShiftList, tipText='Select the shift list to take input chemical shifts from') self.shiftListPulldown.grid(row=0, column=3, sticky='w') label = Label(frame, text='Type') label.grid(row=0, column=4, sticky='w') self.scriptPulldown = PulldownList( frame, texts=SCRIPT_TEXTS, callback=self.changeScript, tipText='Select the algorithm script for this chain') self.scriptPulldown.grid(row=0, column=5, sticky='w') self.pHLabel = Label(frame, text='pH') self.pHLabel.grid(row=0, column=6, sticky='w') self.pHPulldown = PulldownList( frame, texts=SCRIPT_PHS, tipText='Select the pH to make the prediction for') self.pHPulldown.grid(row=0, column=7, sticky='w') row += 1 # BOTTOM LEFT FRAME frame = LabelFrame(guiFrame, text='Chemical Shift Predictions') frame.grid(row=row, column=0, sticky='nsew') frame.grid_columnconfigure(0, weight=1) frame.grid_rowconfigure(0, weight=1) tipTexts = [ 'Residue number in chain', 'Residue type code', 'Atom name', 'Actual shift (first one it finds for atom in chosen shiftList)', 'CamCoil predicted shift', 'Predicted - Actual' ] headingList = [ 'Res\nNum', 'Res\nType', 'Atom\nName', 'Actual\nShift', 'Predicted\nShift', 'Difference' ] n = len(headingList) editWidgets = n * [None] editGetCallbacks = n * [None] editSetCallbacks = n * [None] self.predictionMatrix = ScrolledMatrix( frame, headingList=headingList, tipTexts=tipTexts, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks) self.predictionMatrix.grid(row=0, column=0, sticky='nsew') row += 1 tipTexts = [ 'Run the CamCoil method to predict chemical shifts from sequence' ] texts = ['Run CamCoil Prediction!'] commands = [self.runCamCoil] self.buttonList = createDismissHelpButtonList(guiFrame, texts=texts, commands=commands, help_url=self.help_url, expands=True, tipTexts=tipTexts) self.buttonList.grid(row=row, column=0) self.update() self.notify(self.registerNotify) def destroy(self): self.notify(self.unregisterNotify) BasePopup.destroy(self) def notify(self, notifyfunc): for func in ('__init__', 'delete'): notifyfunc(self.updateChainPulldown, 'ccp.molecule.MolSystem.Chain', func) for func in ('setValue', ): notifyfunc(self.updatePredictionMatrixAfter, 'ccp.nmr.Nmr.Shift', func) def update(self): self.updateShiftListPulldown() self.updateChainPulldown() self.updatePredictionMatrixAfter() def runCamCoil(self): chain = self.chain script = self.scriptPulldown.getText() if script == LFP_SCRIPT: pH = '' else: pH = self.pHPulldown.getText() if not chain: showError('Cannot Run CamCoil', 'Please specify a chain.', parent=self) return self.predictionDict[chain] = runCamCoil(chain, pH=pH, script=script) self.updatePredictionMatrix() def updatePredictionMatrixAfter(self, index=None, text=None): self.after_idle(self.updatePredictionMatrix) def updatePredictionMatrix(self): objectList = [] textMatrix = [] chain = self.chain shiftList = self.shiftList if chain: atomShiftDict = self.predictionDict.get(chain, {}) for residue in chain.sortedResidues(): for atom in residue.sortedAtoms(): currShift = shiftList and findFirstAtomShiftInShiftList( atom, shiftList) value = currShift and currShift.value predShift = atomShiftDict.get(atom) if currShift and predShift: delta = predShift - value else: delta = None data = [ residue.seqCode, residue.ccpCode, atom.name, value, predShift, delta ] textMatrix.append(data) objectList.append(atom) self.predictionMatrix.update(textMatrix=textMatrix, objectList=objectList) def changeChain(self, chain): if chain is not self.chain: self.chain = chain self.updatePredictionMatrixAfter() def changeShiftList(self, shiftList): if shiftList is not self.shiftList: self.shiftList = shiftList self.updatePredictionMatrixAfter() def changeScript(self, script): if script == LFP_SCRIPT: self.pHLabel.grid_forget() self.pHPulldown.grid_forget() else: self.pHLabel.grid(row=0, column=6, sticky='w') self.pHPulldown.grid(row=0, column=7, sticky='w') def updateChainPulldown(self, obj=None): index = 0 names = [] chains = [] chain = self.chain for molSystem in self.project.molSystems: msCode = molSystem.code for chainA in molSystem.chains: residues = chainA.residues if not residues: continue for residue in residues: # Must have at least one protein residue if residue.molType == 'protein': names.append('%s:%s' % (msCode, chainA.code)) chains.append(chainA) break if chains: if chain not in chains: chain = chains[0] index = chains.index(chain) else: chain = None if chain is not self.chain: self.chain = chain self.updatePredictionMatrixAfter() self.chainPulldown.setup(names, chains, index) def updateShiftListPulldown(self, obj=None): index = 0 names = [] shiftLists = getShiftLists(self.nmrProject) if shiftLists: if self.shiftList not in shiftLists: self.shiftList = shiftLists[0] index = shiftLists.index(self.shiftList) names = ['%s:%d' % (sl.name, sl.serial) for sl in shiftLists] else: self.shiftList = None self.shiftListPulldown.setup(names, shiftLists, index)
class RepositoryFrame(Frame): def __init__(self, guiParent, basePopup): # Base popup required to handle notification of data model changes # e.g. new peak lists, so that the GUI can update to the latest # state self.basePopup = basePopup self.guiParent = guiParent # should the screen autorefresh self.autoRefresh = False # add this to shortcuts to ease navigation self.basePopup.frameShortcuts['Repository'] = self # get a port proxy instance # this should probably belong to the repository directly # or else should belong in a dictionary in the main gui # layer when it can be picked up easily # FIXME JMCI # need to work out how to get a number of these! loc = SharedBeanServiceLocator() self.port = loc.getSharedBean() self.registerNotify = basePopup.registerNotify self.unregisterNotify = basePopup.unregisterNotify Frame.__init__(self, guiParent) # set up the grid self.grid_columnconfigure(0, weight=0, minsize=20) self.grid_columnconfigure(1, weight=1, minsize=10) self.grid_columnconfigure(2, weight=0, minsize=20) self.grid_rowconfigure(0, weight=0, minsize=5) self.grid_rowconfigure(1, weight=0, minsize=0) self.grid_rowconfigure(2, weight=0, minsize=10) self.grid_rowconfigure(3, weight=0, minsize=10) self.grid_rowconfigure(4, weight=0, minsize=10) self.grid_rowconfigure(5, weight=1, minsize=10) self.grid_rowconfigure(6, weight=0, minsize=10) self.grid_rowconfigure(7, weight=0, minsize=10) # widgets for view when no data self.noDataWidgets = [] self.noRepLabel = Label(self, text='No repository currently selected.') self.noDataWidgets.append(self.noRepLabel) # widgets for view when current repository set self.dataWidgets = [] self.repTitle = Label(self, text='Repository:', font='Helvetica16') self.dataWidgets.append(self.repTitle) #self.repLabel = Label(self,text='All Projects in Repository') #self.dataWidgets.append(self.repLabel) self.repTree = Tree(self, doubleCallback=self.goto_project_tab) self.dataWidgets.append(self.repTree) sel = 1 if self.autoRefresh: sel = 0 self.autoRefreshSwitchLabel = Label(self, text='Auto Refresh') self.autoRefreshSwitch = RadioButtons(self, ['on', 'off'], select_callback=self.set_refresh, selected_index=sel) # need to decide whether this is static or not r_button_texts = [ 'Add to Basket', ' Import ', ' Export ', ' Refresh ', 'Properties' ] r_button_cmds = [ self.tmpCall, self.import_project, self.export_project, self.drawFrame, self.goto_project_tab ] self.rep_button_list = ButtonList(self, r_button_texts, r_button_cmds) self.dataWidgets.append(self.rep_button_list) self.filterFrame = FilterFrame(self, self.basePopup, text='Filter') self.dataWidgets.append(self.filterFrame) baskets = ('basket1', 'basket2', 'basket3') self.basketSelect = PulldownList(self, self.tmpCall, baskets) self.dataWidgets.append(self.basketSelect) basketElements = ('1ay3', '1ay7') self.basketList = ScrolledListbox(self, basketElements, 25, 18) self.dataWidgets.append(self.basketList) b_button_texts = ['Remove from Basket', 'New Basket'] b_button_cmds = [self.tmpCall, self.tmpCall] self.b_button_list = ButtonList(self, b_button_texts, b_button_cmds) self.dataWidgets.append(self.b_button_list) # draw if not automatically refreshing if sel == 1: self.drawFrame() # set up a loop self.refresh() def drawFrame(self): if self.basePopup.repList.currentRepository == None: for widget in self.dataWidgets: widget.grid_remove() self.noRepLabel.grid(row=1, column=1, sticky='n') else: self.repository = self.basePopup.repList.currentRepository for widget in self.noDataWidgets: widget.grid_remove() # Column headers # want to have a general admin panel; current repository, button to change, info # about login and so on self.repTitle.set('Repository: ' + self.repository.user + '@' + self.repository.name + ' ( ' + self.repository.connect + ' )') self.repTitle.grid(row=1, column=1, sticky='w') # self.repLabel.grid(row=2, column=1, sticky='w') # Repository block #grid and update self.repTree.grid(row=3, column=1, rowspan=3, sticky='nsew') self.updateRepTree() self.autoRefreshSwitchLabel.grid(row=6, column=1, padx=10, sticky='w') self.autoRefreshSwitch.grid(row=6, column=1, padx=10, sticky='e') # the buttons for functionality in repository area self.rep_button_list.grid(row=7, column=1, padx=3, sticky='ew') # filter frame self.filterFrame.grid(row=3, column=2, stick='nsew') # list of baskest. Will callback to change current basket self.basketSelect.grid(row=4, column=2, padx=10, stick='w') # lists projects per basket self.basketList.grid(row=5, rowspan=2, column=2, padx=10, stick='nsew') self.b_button_list.grid(row=7, column=2, padx=5, stick='ew') def openLink(self, node): tree = node.tree par = node.object # this should be associated with the repository object request = getList() # FIXME JMCI # 1. Potentially need to traverse up the node tree # 2. Currently there is bug in expansion/contraction # 3. Search needs to work off proxy method (not off # generated stubs # pass the parent name for now. may need other criteria later h1 = {'name': par.__str__()} h2 = {'project': h1} wsstr_in = WSString(h2) request._arg0 = 'org.pimslims.applet.server.ProjectVersionBean' request._arg1 = 'getListWithFields' request._arg2 = wsstr_in.str # get the response response = self.port.getList(request) # currently we just get a list of numbers. May want to send # all the fields for every object too to cut down on db calls wsstr_out = WSString(response._return) ss = wsstr_out.getStruct() for hm in ss: print 'handling ', hm, ', ', hm['status'].__str__() #if hm['status'].__str__() == 'AVAILABLE': icon = 'text-x-generic' #else: # icon='emblem-readonly' pv = par.__str__() + '::' + hm['versionTag'].__str__() obj = pv text = hm['versionTag'].__str__() callback = None tree.add(node, obj, text, icon, callback) def set_refresh(self, text): print 'setting refresh ', text if text == 'on': self.autoRefresh = True else: self.autoRefresh = False def refresh(self): if self.autoRefresh: print 'refreshing frame ', time.time() self.drawFrame() self.after(5000, self.refresh) # FIME JMCI # it would probably be a good idea to have a method that allowed # a user to unlock a given project version. Would need to check # permissions very carefully, though def unlock_project_version(self): pass def goto_project_tab(self, node=None): if node is None: # get the selected node selected_nodes = self.repTree.getSelectedNodes() # restrict the choice to one node = selected_nodes[0] # we really need to traverse the tree a lot more carefully pv = node.object.__str__() print 'GOTO PROJECT ', pv, ', ', node, ', ', node.object pat = re.compile('::') matcher = pat.search(pv, 0) name = pv[0:matcher.start()] versionTag = pv[matcher.end():] print 'GOTO PROJECT ', name, ', ', versionTag #versionTag = node.object.__str__() #name = node.parent.object.__str__() #print node.__dict__ #print node.parent.__dict__ self.basePopup.repList.currentRepository.currentProjectName = name self.basePopup.repList.currentRepository.currentVersionTag = versionTag # this is going to have to be keyed on a natural key that then # gets set into the criteria of a ws query # hack for now. build in later. Essentially, we have to handle two # cases; that of a specific projectVersion request and that of a # request on a project alone. Probably the best behaviour would be # for the project-only request to default to the latest trunk # version of the project and the most elegent way of triggering # this would probably be to have this as a property of the project # returned in the main hash from Project.getFields(). self.basePopup.tabbedFrame.select(1) if self.basePopup.frameShortcuts.has_key('Project'): self.basePopup.frameShortcuts['Project'].drawFrame() def import_project(self): # FIXME JMCI # need to preserve the current status. Take default from the # Wms layer rootDir = self.basePopup.repList.current_import_dir fileSelectPopup = FileSelectPopup(self, None, rootDir) new_project_dir = fileSelectPopup.getDirectory() self.basePopup.repList.current_import_dir = new_project_dir print 'in import project with directory', new_project_dir idx = new_project_dir.rfind('/') new_project_name = new_project_dir[idx + 1:] print 'in import project with project', new_project_name # FIXME # need to set the project version number somewhere. For now set to none # and this gets picked up and set to a default 1.1 later self.repository.import_project(new_project_name, None, new_project_dir) self.drawFrame() def export_project(self): # get the selected node selected_nodes = self.repTree.getSelectedNodes() # restrict the choice to one node = selected_nodes[0] # we really need to traverse the tree a lot more carefully name = node.parent.object.__str__() versionTag = node.object.__str__()[len(name) + 2:] # need to preserve the current status. Take a default from the # Wms layer rootDir = self.basePopup.repList.current_export_dir # This should be obtained from a query box. Hard code for now fileSelectPopup = FileSelectPopup(self, None, rootDir) exp_project_dir = fileSelectPopup.getDirectory() self.basePopup.repList.current_export_dir = exp_project_dir self.repository.export_project(name, versionTag, exp_project_dir) def disconnectRepository(self): # do we need to formally break a connection and destroy the session? # yes we probably should. # need an "are you sure" dialog box # need to protect self.repository.repList.repositories.remove(self.repository) self.grid_remove() for ff in self.guiParent.guiParent.parent.frames[0].children.values(): ff.drawFrame() def tmpCall(self): return # This will need a separate call to the WS with an additional # criterion (picking up the checkout version) def updateRepTree(self): texts = [] icons = [] parents = [] callbacks = [] objects = [] # OK, this would be a good point to access the list # this should be associated with the repository object loc = SharedBeanServiceLocator() port = loc.getSharedBean() request = getList() # these are actually static request._arg0 = 'org.pimslims.applet.server.ProjectBean' request._arg1 = 'getList' request._arg2 = '' # get the response response = port.getList(request) # this is a hack at present. It needs to be written properly wsstr = WSString(response._return) ss = wsstr.getStruct() for strg in ss: texts.append(strg) icons.append('folder') parents.append(None) objects.append(strg) callbacks.append(self.openLink) print 'UPDATE ', ss print 'UPDATE ', len(ss) if len(ss) > 0: print 'UPDATE: updating ' self.repTree.update(parents, objects, texts, icons, callbacks) def administerNotifiers(self, notifyFunc): for func in ('__init__', 'delete', 'setName'): notifyFunc(self.updateAllAfter, 'ccp.nmr.Nmr.Experiment', func) notifyFunc(self.updateAllAfter, 'ccp.nmr.Nmr.DataSource', func) def updateAllAfter(self, obj): self.after_idle(self.updateAll) def updateAll(self, project=None): return def quit(self): self.guiParent.parent.destroy() def destroy(self): self.administerNotifiers(self.basePopup.unregisterNotify) Frame.destroy(self)
class PeakSeparatorGui(BasePopup): """ **Separate Merged Peaks Using Peak Models** The Peak Separator code uses a Markov Chain Monte Carlo search which, using idealised peak shapes, attempts to deconvolve overlapped peak regions into their separate constituent peaks. This routine is also suitable for accurately fitting model shapes to single peaks in order to calculate precise intensities. **Options Peak Separator Parameters** *Min. Number of peaks* is by default set to one, it is not possible to set this to a value less than one. *Max. Number of peaks* is by default set to one, increasing this value allows the search routine to fit more models. The best fit may be found with fewer than the maximum number models. Higher numbers slow the routine, and setting this value to 0 allows the routine to (effectively) fit unlimited peaks. *Only pick positive peaks*. If you are not interested in negative peaks, removing the possibility of fitting negative peaks can reduce search time. *Peak Model* fits the spectra with either a Gaussian peak model or a Lorentzian peak model. **Options Region** *Peak List* choose which peak list newly picked peaks should be added to. Peaks picked using this method will have their details appended with 'PeakSepartor' so you know where they came from. *Region Table* shows which area of the current spectrum is about to be searched. *Add Region*. Once an area of spectra has been highlighted clicking this button will pass it's details on to the Peak Separator. *Reset All* will reset all search parameters. *Separate Peaks* will run the Peak Separator code with your current settings. This may take a few minutes to run, depending on the size of the spectral region being searched, the number of peaks being fitted and the speed of your machine. Please wait while this completes. After a successful Peak Separation run, the found peaks will be added to the selected peak list. These peaks intensties (volume) have been found using the peak model selected. **Advanced Settings Tab** *Rate* affects the speed of the Markov Chain Monte Carlo routine. A smaller value results in longer execution, but possibly higher quality results. The default setting is deemed sensible for the majority of runs. *Line Width* offers a finer degree of control over maximum and minimum peak widths for each dimension. The default values are *very* stupid and could do with re-checking for each experiment. *Re-Pick Entire Peak List* if you would like to use the Peak Separator to repick *every* peak in your peak list, try this option - but note that this may take a very long time! """ def __init__(self, parent, programName='Peak Separator', **kw): self.parent = parent self.programName = programName self.versionInfo = 'Version 0.2' self.help_url = 'http://www.ccpn.ac.uk/' self.window = None self.waiting = False self.rootWindow = None # just used for display - PeakSeparator will not see this self._minSigmaHz = None self._maxSigmaHz = None self.customSigma = False self.rePickPeakList = False self._sampleStartPpm = None self._sampleEndPpm = None try: self.project = parent.project except: pass self.params = PeakSeparatorParams() BasePopup.__init__(self, parent=parent, title=programName, location='+100+100', **kw) if not self.analysisProject: print '&&& init: No analysis project found ...' try: if parent.argumentServer: self.argServer = parent.argumentServer else: print '&&& init: No argument server found...' except: print '&&& init: Test' ########################################################################### def body(self, guiFrame): self.geometry('450x500') guiFrame.grid_rowconfigure(0, weight=1) guiFrame.grid_columnconfigure(0, weight=1) options = ['Peak Separator', 'Advanced Settings'] tabbedFrame = TabbedFrame(guiFrame, options=options) tabbedFrame.grid(row=0, column=0, sticky='nsew') buttons = UtilityButtonList(tabbedFrame.sideFrame, helpUrl=self.help_url) buttons.grid(row=0, column=0, sticky='e') self.tabbedFrame = tabbedFrame frameA, frameB = tabbedFrame.frames # # FrameA : Main Settings # frameA.grid_columnconfigure(1, weight=1) row = 0 # Label row row += 1 div = LabelDivider(frameA, text='Peak Separator Parameters') div.grid(row=row, column=0, columnspan=2, sticky='ew') row += 1 label = Label(frameA, text='Min. number of peaks:') label.grid(row=row, column=0, sticky='w') self.minPeaksEntry = IntEntry(frameA, returnCallback=self.applyChange, width=10, \ tipText='Minimum number of peaks to find (must be > 0)') self.minPeaksEntry.grid(row=row, column=1, sticky='n') self.minPeaksEntry.bind('<Leave>', self.applyChange, '+') row += 1 label = Label(frameA, text='Max. number of peaks:') label.grid(row=row, column=0, sticky='w') self.maxPeaksEntry = IntEntry(frameA, returnCallback=self.applyChange, width=10, \ tipText='Maximum number of peaks to find (0 is unlimited - not recommended)') self.maxPeaksEntry.grid(row=row, column=1, sticky='n') self.maxPeaksEntry.bind('<Leave>', self.applyChange, '+') row += 1 label = Label(frameA, text='Only pick positive peaks:') label.grid(row=row, column=0, sticky='w') entries = ['False', 'True'] self.posPeaksButtons = RadioButtons( frameA, entries=entries, select_callback=self.applyChange, direction='horizontal', tipTexts=[ 'Search for both positive and negative intensity peaks', 'Limit search to only positive peaks' ]) self.posPeaksButtons.grid(row=row, column=1, sticky='n') row += 1 label = Label(frameA, text='Peak Model:') label.grid(row=row, column=0, sticky='w') ### G/L Mixture works, but volume calculation involves Gamma function # entries = ['Gaussian', 'Lorentzian', 'G/L Mixture'] entries = ['Gaussian', 'Lorentzian'] self.shapeButtons = RadioButtons( frameA, entries=entries, select_callback=self.applyChange, direction='horizontal', tipTexts=[ 'Choose a Gaussian model peak shape to fit to peaks', 'Choose a Lorentzian model peak shape to fit to peaks' ]) self.shapeButtons.grid(row=row, column=1, sticky='n') row += 1 div = LabelDivider(frameA, text='Region', tipText='Region that search will limit itself to') div.grid(row=row, column=0, columnspan=2, sticky='ew') row += 1 label = Label(frameA, text='Peak List:') label.grid(row=row, column=0, sticky='nw') self.peakListPulldown = PulldownList( frameA, callback=self.setManuallyPickPeakList, tipText='Select which peak list new peaks are to be added to') self.peakListPulldown.grid(row=row, column=1, sticky='nw') # tricky scrolled matrix row += 1 self.regionTable = None frameA.grid_rowconfigure(row, weight=1) headings = ('dim.', 'start (ppm)', 'end (ppm)', 'actual size') self.editDimEntry = IntEntry(self, returnCallback=self.applyChange, width=5, tipText='Dimension number') self.editStartEntry = FloatEntry(self, returnCallback=self.applyChange, width=5, tipText='Search area lower bound') self.editEndEntry = FloatEntry(self, returnCallback=self.applyChange, width=5, tipText='Search area upper bound') editWidgets = [ self.editDimEntry, self.editStartEntry, self.editEndEntry, None ] editGetCallbacks = [None, None, None, None] editSetCallbacks = [None, None, None, None] self.regionTable = ScrolledMatrix(frameA, headingList=headings, multiSelect=False, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, initialRows=5) self.regionTable.grid(row=row, column=0, columnspan=2, sticky='nsew') # Run Button row += 1 texts = ['Add Region'] commands = [self.updateFromRegion] self.addResetButtons = ButtonList( frameA, texts=texts, commands=commands, tipTexts=['Add selected specrtral region']) self.addResetButtons.grid(row=row, column=0, columnspan=2, sticky='ew') row += 1 texts = ['Separate Peaks'] commands = [self.runPeakSeparator] self.runButton = ButtonList(frameA, texts=texts, commands=commands, expands=True, tipTexts=['Run peak search now']) self.runButton.grid(row=row, column=0, columnspan=2, sticky='nsew') # # FrameB : Further Settings # frameB.grid_columnconfigure(0, weight=1) row = 0 div = LabelDivider(frameB, text='Rate:') div.grid(row=row, column=0, columnspan=2, sticky='ew') row += 1 label = Label(frameB, text='Rate of MCMC step size change') label.grid(row=row, column=0, columnspan=1, sticky='w') self.rateEntry = FloatEntry(frameB, returnCallback=self.applyChange, width=10, \ tipText='Rate effects speed of run, smaller values take longer but may produce better results') self.rateEntry.grid(row=row, column=1, sticky='n') self.rateEntry.bind('<Leave>', self.applyChange, '+') self.rateEntry.set(self.params.rate) # tricky scrolled matrix for line width row += 2 div = LabelDivider(frameB, text='Line Width (Hz):') div.grid(row=row, column=0, columnspan=2, sticky='ew') row += 1 label = Label(frameB, text="Descr.") label.grid(row=row, rowspan=2, column=0, sticky='w') row += 1 self.lineWidthTable = None frameB.grid_rowconfigure(row, weight=1) lineWidthHeadings = ('dim.', 'min. σ (Hz)', 'max. σ (Hz)') self.editMinSigmaEntry = FloatEntry(self, returnCallback=self.applyChange, width=5, tipText='Minimum line width (Hz)') self.editMaxSigmaEntry = FloatEntry(self, returnCallback=self.applyChange, width=5, tipText='Maximum line width (Hz)') # self.editDimEntry is also from regionTable initialWidthRows = 4 editLineWidthWidgets = [ None, self.editMinSigmaEntry, self.editMaxSigmaEntry ] editLineWidthGetCallbacks = [None, self.getSigmaMin, self.getSigmaMax] editLineWidthSetCallbacks = [None, self.setSigmaMin, self.setSigmaMax] self.lineWidthTable = ScrolledMatrix( frameB, headingList=lineWidthHeadings, multiSelect=False, editWidgets=editLineWidthWidgets, editGetCallbacks=editLineWidthGetCallbacks, editSetCallbacks=editLineWidthSetCallbacks, initialRows=initialWidthRows) self.lineWidthTable.grid(row=row, column=0, columnspan=2, sticky='nsew') # option to 'repick' exisiting peak list row += initialWidthRows div = LabelDivider(frameB, text='(optional - repick entire peak list)') div.grid(row=row, column=0, columnspan=2, sticky='ew') row += 1 self.repickListPulldown = PulldownList( frameB, callback=self.setRePickPeakList, tipText= 'Select which peak list to repick (new peaks will be put into a new peak list)' ) self.repickListPulldown.grid(row=row, column=0, sticky='nw') texts = ['Repick Peak List'] commands = [self.runRepickPeaks] self.runButton = ButtonList( frameB, texts=texts, commands=commands, expands=True, tipTexts=['Repick selected peak list into a new peak list.']) self.runButton.grid(row=row, column=1, columnspan=1, sticky='nsew') row += 1 div = LabelDivider(frameB) row += 1 texts = ['Separate Peaks'] commands = [self.runPeakSeparator] self.runButton = ButtonList(frameB, texts=texts, commands=commands, expands=True, tipTexts=['Run peak search now']) self.runButton.grid(row=row, column=0, columnspan=2, sticky='nsew') self.setWidgetEntries() self.administerNotifiers(self.registerNotify) def administerNotifiers(self, notifyFunc): for func in ('__init__', 'delete'): notifyFunc(self.updateAfter, 'ccp.nmr.Nmr.PeakList', func) notifyFunc(self.updateAfter, 'ccp.nmr.Nmr.Experiment', 'setName') notifyFunc(self.updateAfter, 'ccp.nmr.Nmr.DataSource', 'setName') def destroy(self): self.administerNotifiers(self.unregisterNotify) BasePopup.destroy(self) ########################################################################### # update parameters from PS Region def updateFromRegion(self): if not self.params.peakList: print '&&& update from region: Need a peak list' return if (self.argServer.parent.currentRegion) == None: showError('No Region', 'Please select a peak region to be separated') return self.rePickPeakList = False getRegionParams(self.params, argServer=self.argServer) if not self.customSigma: self.initSigmaParams() self.setWidgetEntries() ########################################################################### # update parameters from PS PeakList def updateFromPeakList(self): if not self.params.peakList: print '&&& update from peakList: Need a peak list' return getPeakListParams(self.params) if not self.customSigma: self.initSigmaParams() self.setWidgetEntries() ########################################################################### # Run the C library! def runPeakSeparator(self): """ run the peak separator """ # hack for Macs - focus isn't always lost on mouse move # so bind event not always called. Shouldn't affect other OS. self.applyChange() if not self.params.peakList: print '&&& Peak list not yet set' else: # SeparatePeakRoutine(self.params, self.params.peakList, routine='pymc' ) SeparatePeakRoutine(self.params, self.params.peakList, routine='bayesys') def runRepickPeaks(self): """ Run the Peak Separator on entire chosen peak list """ # hack for Macs - focus isn't always lost on mouse move # so bind event not always called. Shouldn't affect other OS. self.applyChange() if not self.params.peakList: print '&&& Peak list not yet set' else: SeparatePeaksInPeakList(self.params) ########################################################################### def setWidgetEntries(self): ### Page One widgets self.minPeaksEntry.set(self.params.minAtoms) self.maxPeaksEntry.set(self.params.maxAtoms) if self.params.positivePeaks == 1: self.posPeaksButtons.set('True') # only pick pos peaks else: self.posPeaksButtons.set('False') # do something fancy if different shapes for each dim! n = self.params.peakShape - 3 # shape is only 3, 4, (5) self.shapeButtons.setIndex(n) if self.project is not None: self.updatePeakListList() self.updateSpectrumWindow() if self.params.sampleStart and self.params.peakList: if not self.rePickPeakList: objectList = [] textMatrix = [] if len(self.params.samplePpmStart) != self.params.Ndim: return for i in range(self.params.Ndim): dim_entry = [] dim_entry.append('%2d' % (i + 1)) dim_entry.append('%7.3f' % self.params.samplePpmStart[i]) dim_entry.append('%7.3f' % self.params.samplePpmEnd[i]) dim_entry.append('%3d' % self.params.sampleSize[i]) textMatrix.append(dim_entry) self.regionTable.update(textMatrix=textMatrix, objectList=objectList) ### Page Two widgets self.rateEntry.set(self.params.rate) if self.params.peakList and self.params.Ndim: textMatrix = [] objectList = [] for i in range(self.params.Ndim): if self.params.isFreqDim[i]: dim_entry = [] objectList.append(i) dim_entry.append('%2d' % (i + 1)) dim_entry.append('%7.3f' % self._minSigmaHz[i]) dim_entry.append('%7.3f' % self._maxSigmaHz[i]) textMatrix.append(dim_entry) self.lineWidthTable.update(textMatrix=textMatrix, objectList=objectList) def applyChange(self, *event): """ Upon change, add settings to params """ # Page One apply changes self.params.minAtoms = self.minPeaksEntry.get() self.params.maxAtoms = self.maxPeaksEntry.get() if self.posPeaksButtons.get() == 'True': # asked only pick pos peaks self.params.positivePeaks = 1 else: self.params.positivePeaks = 0 # do something fancy if different shapes for each dim! n = self.shapeButtons.getIndex() # shape is only 3, 4, (5) self.params.peakShape = n + 3 # Page Two apply changes self.params.rate = float(self.rateEntry.get()) self.updateSigmaParams() ########################################################################### # Peak list functions provide PeakSeparator some inherited params def getPeakListList(self): """ given a spectrum, get list of peak lists """ project = self.project peakLists = [] for experiment in self.nmrProject.experiments: for spectrum in experiment.dataSources: for peakList in spectrum.peakLists: peakLists.append([ '%s:%s:%d' % (experiment.name, spectrum.name, peakList.serial), peakList ]) peakLists.sort() return peakLists def updatePeakListList(self): """ set the peaklist list in the pulldown menu """ peakListData = self.getPeakListList() index = -1 names = [] peakList = self.params.peakList if peakListData: names = [x[0] for x in peakListData] peakLists = [x[1] for x in peakListData] if peakList not in peakLists: peakList = peakLists[0] index = peakLists.index(peakList) else: peakList = None peakLists = [] if peakList is not self.params.peakList: self.params.peakList = peakList self.peakListPulldown.setup(names, peakLists, index) self.repickListPulldown.setup(names, peakLists, index) def setRePickPeakList(self, peakList): """ Set the peak list to be repicked (and hit a Flag) """ self.rePickPeakList = True self.setPeakList(peakList) def setManuallyPickPeakList(self, peakList): """ Set the peak list to add new peaks to (and hit a Flag) """ self.rePickPeakList = False self.setPeakList(peakList) def setPeakList(self, peakList): """ Sets the Peak List """ if peakList is not self.params.peakList: self.params.peakList = peakList # # interrogate the peak list and get all the usefull parameters out self.updateFromPeakList() self.updateSpectrumWindow() self.setWidgetEntries() ########################################################################### # TBD I suspect this is for matching region with peak list, but may be obsolete now def getSpectrumWindowList(self): """ get list of windows which spectrum could be in """ windows = {} if self.params.peakList: views = getSpectrumViews(self.params.peakList.dataSource) for view in views: windows[view.spectrumWindowPane.spectrumWindow] = None return [[w.name, w] for w in windows.keys()] def updateSpectrumWindow(self): """ update the spectrum window """ windowData = self.getSpectrumWindowList() index = -1 names = [] window = self.rootWindow if windowData: names = [x[0] for x in windowData] windows = [x[1] for x in windowData] if window not in windows: window = windows[0] index = windows.index(window) else: window = None windows = [] if window is not self.rootWindow: self.rootWindow = window ########################################################################### # get and set sigma stuff def setSigmaMin(self, dim): value = self.editMinSigmaEntry.get() self._minSigmaHz[dim] = value # dont go and re-write users settings self.customSigma = True # make sure changes are in params object self.updateSigmaParams(dim) self.setWidgetEntries() def getSigmaMin(self, dim): if dim is not None: self.editMinSigmaEntry.set(self._minSigmaHz[dim]) def setSigmaMax(self, dim): value = self.editMaxSigmaEntry.get() self._maxSigmaHz[dim] = value # dont go and re-write users settings self.customSigma = True # make sure changes are in params object self.updateSigmaParams(dim) self.setWidgetEntries() def getSigmaMax(self, dim): if dim is not None: self.editMaxSigmaEntry.set(self._maxSigmaHz[dim]) def updateSigmaParams(self, dim=None): """ updateSigmaParams Just updates the parameters (params obj) for sigma values. If dim is None, do this for each dim """ dataDimRefs = self.params.dataDimRefs if not dataDimRefs: return if not self.params.minSigma or len( self.params.minSigma) != self.params.Ndim: self.params.minSigma = [0.] * self.params.Ndim if not self.params.maxSigma or len( self.params.maxSigma) != self.params.Ndim: self.params.maxSigma = [0.] * self.params.Ndim def updateSigmaParam(dim, dataDimRefs): """ Convert and update sigma for dim """ if self.params.isFreqDim[dim]: # note factor of two! self.params.minSigma[dim] = self.rHz2pnt( self._minSigmaHz[dim], dataDimRefs[dim]) / 2. self.params.maxSigma[dim] = self.rHz2pnt( self._maxSigmaHz[dim], dataDimRefs[dim]) / 2. else: self.params.minSigma[dim] = 1.0 self.params.maxSigma[dim] = 1.0 if dim: updateSigmaParam(dim, dataDimRefs) else: for dim in range(self.params.Ndim): updateSigmaParam(dim, dataDimRefs) # utility functions for sigma values def pnt2rHz(self, point, dataDimRef): """ Point to relative Hz frequency relative to frequency at Zeroeth point Necessary when (for example) looking for width of peak in Hz """ assert point, dataDimRef sigmaBase = pnt2hz(0, dataDimRef) sigmaHz = pnt2hz(point, dataDimRef) return abs(sigmaHz - sigmaBase) def rHz2pnt(self, freq, dataDimRef): """ Relative Hz to point frequency relative to frequency at Zeroeth point Necessary when (for example) looking for width of peak in Hz """ assert freq, dataDimRef sigmaBase = hz2pnt(0, dataDimRef) sigmaPoint = hz2pnt(freq, dataDimRef) return abs(sigmaPoint - sigmaBase) def initSigmaParams(self): """ Set some initial default values for sigma """ self._minSigmaHz = [] self._maxSigmaHz = [] if self.params.Ndim: for dim in range(self.params.Ndim): self._minSigmaHz.append(6.) self._maxSigmaHz.append(28.) ########################################################################### def updateAll(self): self.updateSpectrumWindow() self.updatePeakListList() self.waiting = False def updateAfter(self, obj=None): if self.waiting: return else: self.waiting = True self.after_idle(self.updateAll)
class AddContourFilePopup(BasePopup): """ **Add Existing Contour Files to Project** The purpose of this dialog is to allow the user to add pre-existing contour files to the project. Contour files only depend on the spectrum data so the same contour files can be used across multiple projects, and that is the reason this dialog might be used. See also: `Spectrum Contour Files`_, `Creating Contour Files`_. .. _`Spectrum Contour Files`: EditContourFilesPopup.html .. _`Creating Contour Files`: CreateContourFilePopup.html """ def __init__(self, parent, *args, **kw): self.spectrum = None BasePopup.__init__(self, parent=parent, title='Add existing contour file', **kw) def body(self, master): self.geometry('600x130') master.grid_columnconfigure(1, weight=1) for n in range(5): master.grid_rowconfigure(n, weight=1) row = 0 label = Label(master, text='Spectrum: ') label.grid(row=row, column=0, sticky='e') tipText = 'The spectrum for which the contour file is being added' self.expt_spectrum = PulldownList(master, callback=self.updateContourDir, tipText=tipText) self.expt_spectrum.grid(row=row, column=1, sticky='w') row = row + 1 tipText = 'The location of the directory where contour files are stored on disk' label = Label(master, text='Contour dir: ') label.grid(row=row, column=0, sticky='e') self.dir_label = Label(master, text='', tipText=tipText) self.dir_label.grid(row=row, column=1, sticky='w') row = row + 1 label = Label( master, text= '(file will be copied into Contour dir if it is not already in there)' ) label.grid(row=row, column=1, sticky='w') row = row + 1 tipText = 'Browse for a file store contour data' button = Button(master, text='File name: ', command=self.selectFile, tipText=tipText) button.grid(row=row, column=0, sticky='e') tipText = 'Enter the name of the file to store contour data' self.file_entry = Entry(master, tipText=tipText) self.file_entry.grid(row=row, column=1, sticky='ew') row = row + 1 texts = ['Add File'] commands = [self.addFile] tipTexts = [ 'Use the selected contour file in the current project, copying it to the contour directory if required', ] self.buttons = UtilityButtonList(master, texts=texts, doClone=False, tipTexts=tipTexts, commands=commands, helpUrl=self.help_url) self.buttons.grid(row=row, column=0, columnspan=2, sticky='ew') self.curateNotifiers(self.registerNotify) self.updateSpectrum() def destroy(self): self.curateNotifiers(self.unregisterNotify) BasePopup.destroy(self) def curateNotifiers(self, notifyFunc): for clazz in ('Experiment', 'DataSource'): for func in ('__init__', 'delete', 'setName'): notifyFunc(self.updateNotifier, 'ccp.nmr.Nmr.%s' % clazz, func) def updateSpectrum(self, spectrum=None): if not spectrum: spectrum = self.spectrum spectra = self.parent.getSpectra() if spectra: if spectrum not in spectra: spectrum = spectra[0] index = spectra.index(spectrum) names = ['%s:%s' % (x.experiment.name, x.name) for x in spectra] else: index = 0 names = [] self.expt_spectrum.setup(names, spectra, index) self.updateContourDir(spectrum) def updateNotifier(self, *extra): self.updateSpectrum() def updateContourDir(self, spectrum): if spectrum is self.spectrum: return self.spectrum = spectrum if spectrum: path = spectrum.analysisSpectrum.contourDir.dataLocation else: path = '' self.dir_label.set(path) def selectFile(self): spectrum = self.spectrum if spectrum: directory = spectrum.analysisSpectrum.contourDir.dataLocation else: directory = os.getcwd() popup = FileSelectPopup(self, directory=directory) fileName = popup.getFile() popup.destroy() if fileName: self.file_entry.set(fileName) def addFile(self): spectrum = self.spectrum if not spectrum: return dataStore = spectrum.dataStore if not dataStore: showError('No dataStore', 'Spectrum does not have associated dataStore', parent=self) return if not isinstance(dataStore, BlockedBinaryMatrix): showError('No blockedBinaryMatrix', 'Spectrum dataStore is not a blockedBinaryMatrix', parent=self) return if not dataStore.blockSizes: showError('No blockSize', 'Spectrum dataStore does not have blockSize set', parent=self) return blockSize = list(dataStore.blockSizes) fileName = self.file_entry.get() if not fileName: showError('No filename', 'No filename given', parent=self) return fileName = normalisePath(fileName, makeAbsolute=True) contourDir = spectrum.analysisSpectrum.contourDir.dataLocation if fileName.startswith(contourDir): path = fileName[len(contourDir) + 1:] else: path = os.path.basename(fileName) if not os.path.exists(contourDir): os.makedirs(contourDir) print 'Copying %s to %s' % (fileName, contourDir) shutil.copy(fileName, contourDir) try: header = getStoredContourHeader(fileName) except Exception, e: showError('File error', str(e), parent=self) return if header['ndim'] != spectrum.numDim: showError( 'Number of dimensions', 'Number of dimensions in file (%d) does not match spectrum (%d)' % (header['ndim'], spectrum.numDim), parent=self) return dataDims = spectrum.sortedDataDims() npoints = [x.numPoints for x in dataDims] if header['npoints'] != npoints: showError( 'Number of points', 'Number of points in file (%s) does not match spectrum (%s)' % (header['npoints'], npoints), parent=self) return if header['blockSize'] != blockSize: showError('Block size', 'Block size in file (%s) does not match spectrum (%s)' % (header['blockSize'], blockSize), parent=self) return createStoredContour(spectrum, path, header['xdim'], header['ydim']) showInfo('Added file', 'Successfully added stored contour file', parent=self)
class CingFrame(NmrSimRunFrame): def __init__(self, parent, application, *args, **kw): project = application.project simStore = project.findFirstNmrSimStore(application=APP_NAME) or \ project.newNmrSimStore(application=APP_NAME, name=APP_NAME) self.application = application self.residue = None self.structure = None self.serverCredentials = None self.iCingBaseUrl = DEFAULT_URL self.resultsUrl = None self.chain = None self.nmrProject = application.nmrProject self.serverDone = False NmrSimRunFrame.__init__(self, parent, project, simStore, *args, **kw) # # # # # # New Structure Frame # # # # # self.structureFrame.grid_forget() tab = self.tabbedFrame.frames[0] frame = Frame(tab, grid=(1,0)) frame.expandGrid(2,1) div = LabelDivider(frame, text='Structures', grid=(0,0), gridSpan=(1,2)) label = Label(frame, text='Ensemble: ', grid=(1,0)) self.structurePulldown = PulldownList(frame, callback=self.changeStructure, grid=(1,1)) headingList = ['Model','Use'] editWidgets = [None,None] editGetCallbacks = [None,self.toggleModel] editSetCallbacks = [None,None,] self.modelTable = ScrolledMatrix(frame, grid=(2,0), gridSpan=(1,2), callback=self.selectStructModel, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, headingList=headingList) texts = ['Activate Selected','Inactivate Selected'] commands = [self.activateModels, self.disableModels] buttons = ButtonList(frame, texts=texts, commands=commands, grid=(3,0), gridSpan=(1,2)) # # # # # # Submission frame # # # # # # tab = self.tabbedFrame.frames[1] tab.expandGrid(1,0) frame = LabelFrame(tab, text='Server Job Submission', grid=(0,0)) frame.expandGrid(None,2) srow = 0 label = Label(frame, text='iCing URL:', grid=(srow, 0)) urls = [DEFAULT_URL,] self.iCingBaseUrlPulldown = PulldownList(frame, texts=urls, objects=urls, index=0, grid=(srow,1)) srow +=1 label = Label(frame, text='Results File:', grid=(srow, 0)) self.resultFileEntry = Entry(frame, bd=1, text='', grid=(srow,1), width=50) self.setZipFileName() button = Button(frame, text='Choose File', bd=1, sticky='ew', command=self.chooseZipFile, grid=(srow, 2)) srow +=1 label = Label(frame, text='Results URL:', grid=(srow, 0)) self.resultUrlEntry = Entry(frame, bd=1, text='', grid=(srow,1), width=50) button = Button(frame, text='View Results HTML', bd=1, sticky='ew', command=self.viewHtmlResults, grid=(srow, 2)) srow +=1 texts = ['Submit Project!', 'Check Run Status', 'Purge Server Result', 'Download Results'] commands = [self.runCingServer, self.checkStatus, self.purgeCingServer, self.downloadResults] self.buttonBar = ButtonList(frame, texts=texts, commands=commands, grid=(srow, 0), gridSpan=(1,3)) for button in self.buttonBar.buttons[:1]: button.config(bg=CING_BLUE) # # # # # # Residue frame # # # # # # frame = LabelFrame(tab, text='Residue Options', grid=(1,0)) frame.expandGrid(1,1) label = Label(frame, text='Chain: ') label.grid(row=0,column=0,sticky='w') self.chainPulldown = PulldownList(frame, callback=self.changeChain) self.chainPulldown.grid(row=0,column=1,sticky='w') headingList = ['#','Residue','Linking','Decriptor','Use?'] editWidgets = [None,None,None,None,None] editGetCallbacks = [None,None,None,None,self.toggleResidue] editSetCallbacks = [None,None,None,None,None,] self.residueMatrix = ScrolledMatrix(frame, headingList=headingList, multiSelect=True, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, callback=self.selectResidue) self.residueMatrix.grid(row=1, column=0, columnspan=2, sticky = 'nsew') texts = ['Activate Selected','Inactivate Selected'] commands = [self.activateResidues, self.deactivateResidues] self.resButtons = ButtonList(frame, texts=texts, commands=commands,) self.resButtons.grid(row=2, column=0, columnspan=2, sticky='ew') """ # # # # # # Validate frame # # # # # # frame = LabelFrame(tab, text='Validation Options', grid=(2,0)) frame.expandGrid(None,2) srow = 0 self.selectCheckAssign = CheckButton(frame) self.selectCheckAssign.grid(row=srow, column=0,sticky='nw' ) self.selectCheckAssign.set(True) label = Label(frame, text='Assignments and shifts') label.grid(row=srow,column=1,sticky='nw') srow += 1 self.selectCheckResraint = CheckButton(frame) self.selectCheckResraint.grid(row=srow, column=0,sticky='nw' ) self.selectCheckResraint.set(True) label = Label(frame, text='Restraints') label.grid(row=srow,column=1,sticky='nw') srow += 1 self.selectCheckQueen = CheckButton(frame) self.selectCheckQueen.grid(row=srow, column=0,sticky='nw' ) self.selectCheckQueen.set(False) label = Label(frame, text='QUEEN') label.grid(row=srow,column=1,sticky='nw') srow += 1 self.selectCheckScript = CheckButton(frame) self.selectCheckScript.grid(row=srow, column=0,sticky='nw' ) self.selectCheckScript.set(False) label = Label(frame, text='User Python script\n(overriding option)') label.grid(row=srow,column=1,sticky='nw') self.validScriptEntry = Entry(frame, bd=1, text='') self.validScriptEntry.grid(row=srow,column=2,sticky='ew') scriptButton = Button(frame, bd=1, command=self.chooseValidScript, text='Browse') scriptButton.grid(row=srow,column=3,sticky='ew') """ # # # # # # # # # # self.update(simStore) self.administerNotifiers(application.registerNotify) def downloadResults(self): if not self.run: msg = 'No current iCing run' showWarning('Failure', msg, parent=self) return credentials = self.serverCredentials if not credentials: msg = 'No current iCing server job' showWarning('Failure', msg, parent=self) return fileName = self.resultFileEntry.get() if not fileName: msg = 'No save file specified' showWarning('Failure', msg, parent=self) return if os.path.exists(fileName): msg = 'File %s already exists. Overwite?' % fileName if not showOkCancel('Query', msg, parent=self): return url = self.iCingBaseUrl iCingUrl = self.getServerUrl(url) logText = iCingRobot.iCingFetch(credentials, url, iCingUrl, fileName) print logText msg = 'Results saved to file %s\n' % fileName msg += 'Purge results from iCing server?' if showYesNo('Query',msg, parent=self): self.purgeCingServer() def getServerUrl(self, baseUrl): iCingUrl = os.path.join(baseUrl, 'icing/serv/iCingServlet') return iCingUrl def viewHtmlResults(self): resultsUrl = self.resultsUrl if not resultsUrl: msg = 'No current iCing results URL' showWarning('Failure', msg, parent=self) return webBrowser = WebBrowser(self.application, popup=self.application) webBrowser.open(self.resultsUrl) def runCingServer(self): if not self.project: return run = self.run if not run: msg = 'No CING run setup' showWarning('Failure', msg, parent=self) return structure = self.structure if not structure: msg = 'No structure ensemble selected' showWarning('Failure', msg, parent=self) return ensembleText = getModelsString(run) if not ensembleText: msg = 'No structural models selected from ensemble' showWarning('Failure', msg, parent=self) return residueText = getResiduesString(structure) if not residueText: msg = 'No active residues selected in structure' showWarning('Failure', msg, parent=self) return url = self.iCingBaseUrlPulldown.getObject() url.strip() if not url: msg = 'No iCing server URL specified' showWarning('Failure', msg, parent=self) self.iCingBaseUrl = None return msg = 'Submit job now? You will be informed when the job is done.' if not showOkCancel('Confirm', msg, parent=self): return self.iCingBaseUrl = url iCingUrl = self.getServerUrl(url) self.serverCredentials, results, tarFileName = iCingRobot.iCingSetup(self.project, userId='ccpnAp', url=iCingUrl) if not results: # Message already issued on failure self.serverCredentials = None self.resultsUrl = None self.update() return else: credentials = self.serverCredentials os.unlink(tarFileName) entryId = iCingRobot.iCingProjectName(credentials, iCingUrl).get(iCingRobot.RESPONSE_RESULT) baseUrl, htmlUrl, logUrl, zipUrl = iCingRobot.getResultUrls(credentials, entryId, url) self.resultsUrl = htmlUrl # Save server data in this run for persistence setRunParameter(run, iCingRobot.FORM_USER_ID, self.serverCredentials[0][1]) setRunParameter(run, iCingRobot.FORM_ACCESS_KEY, self.serverCredentials[1][1]) setRunParameter(run, ICING_BASE_URL, url) setRunParameter(run, HTML_RESULTS_URL, htmlUrl) self.update() run.inputStructures = structure.sortedModels() # select residues from the structure's chain #iCingRobot.iCingResidueSelection(credentials, iCingUrl, residueText) # Select models from ensemble #iCingRobot.iCingEnsembleSelection(credentials, iCingUrl, ensembleText) # Start the actual run self.serverDone = False iCingRobot.iCingRun(credentials, iCingUrl) # Fetch server progress occasionally, report when done # this function will call itself again and again self.after(CHECK_INTERVAL, self.timedCheckStatus) self.update() def timedCheckStatus(self): if not self.serverCredentials: return if self.serverDone: return status = iCingRobot.iCingStatus(self.serverCredentials, self.getServerUrl(self.iCingBaseUrl)) if not status: #something broke, already warned return result = status.get(iCingRobot.RESPONSE_RESULT) if result == iCingRobot.RESPONSE_DONE: self.serverDone = True msg = 'CING run is complete!' showInfo('Completion', msg, parent=self) return self.after(CHECK_INTERVAL, self.timedCheckStatus) def checkStatus(self): if not self.serverCredentials: return status = iCingRobot.iCingStatus(self.serverCredentials, self.getServerUrl(self.iCingBaseUrl)) if not status: #something broke, already warned return result = status.get(iCingRobot.RESPONSE_RESULT) if result == iCingRobot.RESPONSE_DONE: msg = 'CING run is complete!' showInfo('Completion', msg, parent=self) self.serverDone = True return else: msg = 'CING job is not done.' showInfo('Processing', msg, parent=self) self.serverDone = False return def purgeCingServer(self): if not self.project: return if not self.run: msg = 'No CING run setup' showWarning('Failure', msg, parent=self) return if not self.serverCredentials: msg = 'No current iCing server job' showWarning('Failure', msg, parent=self) return url = self.iCingBaseUrl results = iCingRobot.iCingPurge(self.serverCredentials, self.getServerUrl(url)) if results: showInfo('Info','iCing server results cleared') self.serverCredentials = None self.iCingBaseUrl = None self.serverDone = False deleteRunParameter(self.run, iCingRobot.FORM_USER_ID) deleteRunParameter(self.run, iCingRobot.FORM_ACCESS_KEY) deleteRunParameter(self.run, HTML_RESULTS_URL) else: showInfo('Info','Purge failed') self.update() def chooseZipFile(self): fileTypes = [ FileType('Zip', ['*.zip']), ] popup = FileSelectPopup(self, file_types=fileTypes, file=self.resultFileEntry.get(), title='Results zip file location', dismiss_text='Cancel', selected_file_must_exist=False) fileName = popup.getFile() if fileName: self.resultFileEntry.set(fileName) popup.destroy() def setZipFileName(self): zipFile = '%s_CING_report.zip' % self.project.name self.resultFileEntry.set(zipFile) def selectStructModel(self, model, row, col): self.model = model def selectResidue(self, residue, row, col): self.residue = residue def deactivateResidues(self): for residue in self.residueMatrix.currentObjects: residue.useInCing = False self.updateResidues() def activateResidues(self): for residue in self.residueMatrix.currentObjects: residue.useInCing = True self.updateResidues() def activateModels(self): if self.run: for model in self.modelTable.currentObjects: if model not in self.run.inputStructures: self.run.addInputStructure(model) self.updateModels() def disableModels(self): if self.run: for model in self.modelTable.currentObjects: if model in self.run.inputStructures: self.run.removeInputStructure(model) self.updateModels() def toggleModel(self, *opt): if self.model and self.run: if self.model in self.run.inputStructures: self.run.removeInputStructure(self.model) else: self.run.addInputStructure(self.model) self.updateModels() def toggleResidue(self, *opt): if self.residue: self.residue.useInCing = not self.residue.useInCing self.updateResidues() def updateResidues(self): if self.residue and (self.residue.topObject is not self.structure): self.residue = None textMatrix = [] objectList = [] colorMatrix = [] if self.chain: chainCode = self.chain.code for residue in self.chain.sortedResidues(): msResidue = residue.residue if not hasattr(residue, 'useInCing'): residue.useInCing = True if residue.useInCing: colors = [None, None, None, None, CING_BLUE] use = 'Yes' else: colors = [None, None, None, None, None] use = 'No' datum = [residue.seqCode, msResidue.ccpCode, msResidue.linking, msResidue.descriptor, use,] textMatrix.append(datum) objectList.append(residue) colorMatrix.append(colors) self.residueMatrix.update(objectList=objectList, textMatrix=textMatrix, colorMatrix=colorMatrix) def updateChains(self): index = 0 names = [] chains = [] chain = self.chain if self.structure: chains = self.structure.sortedCoordChains() names = [chain.code for chain in chains] if chains: if chain not in chains: chain = chains[0] index = chains.index(chain) self.changeChain(chain) self.chainPulldown.setup(names, chains, index) def updateStructures(self): index = 0 names = [] structures = [] structure = self.structure if self.run: model = self.run.findFirstInputStructure() if model: structure = model.structureEnsemble structures0 = [(s.ensembleId, s) for s in self.project.structureEnsembles] structures0.sort() for eId, structure in structures0: name = '%s:%s' % (structure.molSystem.code, eId) structures.append(structure) names.append(name) if structures: if structure not in structures: structure = structures[-1] index = structures.index(structure) self.changeStructure(structure) self.structurePulldown.setup(names, structures, index) def updateModels(self): textMatrix = [] objectList = [] colorMatrix = [] if self.structure and self.run: used = self.run.inputStructures for model in self.structure.sortedModels(): if model in used: colors = [None, CING_BLUE] use = 'Yes' else: colors = [None, None] use = 'No' datum = [model.serial,use] textMatrix.append(datum) objectList.append(model) colorMatrix.append(colors) self.modelTable.update(objectList=objectList, textMatrix=textMatrix, colorMatrix=colorMatrix) def changeStructure(self, structure): if self.project and (self.structure is not structure): self.project.currentEstructureEnsemble = structure self.structure = structure if self.run: self.run.inputStructures = structure.sortedModels() self.updateModels() self.updateChains() def changeChain(self, chain): if self.project and (self.chain is not chain): self.chain = chain self.updateResidues() def chooseValidScript(self): # Prepend default Cyana file extension below fileTypes = [ FileType('Python', ['*.py']), ] popup = FileSelectPopup(self, file_types = fileTypes, title='Python file', dismiss_text='Cancel', selected_file_must_exist = True) fileName = popup.getFile() self.validScriptEntry.set(fileName) popup.destroy() def updateAll(self, project=None): if project: self.project = project self.nmrProject = project.currentNmrProject simStore = project.findFirstNmrSimStore(application='CING') or \ project.newNmrSimStore(application='CING', name='CING') else: simStore = None if not self.project: return self.setZipFileName() if not self.project.currentNmrProject: name = self.project.name self.nmrProject = self.project.newNmrProject(name=name) else: self.nmrProject = self.project.currentNmrProject self.update(simStore) def update(self, simStore=None): NmrSimRunFrame.update(self, simStore) run = self.run urls = [DEFAULT_URL,] index = 0 if run: userId = getRunParameter(run, iCingRobot.FORM_USER_ID) accessKey = getRunParameter(run, iCingRobot.FORM_ACCESS_KEY) if userId and accessKey: self.serverCredentials = [(iCingRobot.FORM_USER_ID, userId), (iCingRobot.FORM_ACCESS_KEY, accessKey)] url = getRunParameter(run, ICING_BASE_URL) if url: htmlUrl = getRunParameter(run, HTML_RESULTS_URL) self.iCingBaseUrl = url self.resultsUrl = htmlUrl # May be None self.resultUrlEntry.set(self.resultsUrl) if self.iCingBaseUrl and self.iCingBaseUrl not in urls: index = len(urls) urls.append(self.iCingBaseUrl) self.iCingBaseUrlPulldown.setup(urls, urls, index) self.updateButtons() self.updateStructures() self.updateModels() self.updateChains() def updateButtons(self, event=None): buttons = self.buttonBar.buttons if self.project and self.run: buttons[0].enable() if self.resultsUrl and self.serverCredentials: buttons[1].enable() buttons[2].enable() buttons[3].enable() else: buttons[1].disable() buttons[2].disable() buttons[3].disable() else: buttons[0].disable() buttons[1].disable() buttons[2].disable() buttons[3].disable()
class BrukerPseudoPopup(BasePopup): def __init__(self, parent, params, dim, *args, **kw): self.dim = dim self.params = params BasePopup.__init__(self, parent=parent, title='Bruker Pseudo Data', modal=True, **kw) def body(self, master): pseudoExpts = getSampledDimExperiments(self.parent.nmrProject) master.rowconfigure(0, weight=1) master.rowconfigure(1, weight=1) master.columnconfigure(0, weight=1) tipTexts = ['The experiment is pseudo-N dimensional, with a sampled axis', 'The experiment is the regular kind with only NMR frequency axes'] self.pseudoEntries = [x % len(self.params.npts) for x in PSEUDO_ENTRIES] self.pseudoButton = RadioButtons(master, entries=self.pseudoEntries, select_callback=self.changedPseudoMode, grid=(0,0), sticky='nw', tipTexts=tipTexts) frame = self.pseudoFrame = Frame(master) self.pseudoFrame.grid(row=1, column=0, sticky='nsew') row = 0 if pseudoExpts: tipText = 'Select from existing pseudo nD experiments to copy sampled axis values from' texts = [x.name for x in pseudoExpts] label = Label(frame, text='Existing pseudo expts: ') label.grid(row=row, column=0, sticky='e') self.pseudoList = PulldownList(frame, texts=texts, objects=pseudoExpts, tipText=tipText) self.pseudoList.grid(row=row, column=1, sticky='w') tipText = 'Transfer the sampled axis values from the existing experiment to the new one' Button(frame, text='Copy values down', command=self.copyValues, tipText=tipText, grid=(row,2)) row += 1 npts = self.params.npts[self.dim] tipText = 'Number of data points (planes) along sampled axis' label = Label(frame, text='Number of points: ') label.grid(row=row, column=0, sticky='e') self.nptsEntry = IntEntry(frame, text=npts, tipText=tipText, width=8, grid=(row,1)) tipText = 'Load the values for the sampled axis from a text file containing a list of numeric values' Button(frame, text='Load File', command=self.loadValues, tipText=tipText, grid=(row,2), sticky='ew') row += 1 tipText = 'The values (e.g. T1, T2) corresponding to each data point (plane) along sampled axis' label = Label(frame, text='Point values: ') label.grid(row=row, column=0, sticky='e') self.valueEntry = FloatEntry(frame, isArray=True, tipText=tipText) #minRows = self.params.npts[self.dim] #self.valueEntry = MultiWidget(frame, FloatEntry, callback=None, minRows=minRows, maxRows=None, # options=None, values=[], useImages=False) self.valueEntry.grid(row=row, column=1, columnspan=2, sticky='ew') row += 1 label = Label(frame, text='(requires comma-separated list, of length number of points)') label.grid(row=row, column=1, columnspan=2, sticky='w') row += 1 for n in range(row): frame.rowconfigure(n, weight=1) frame.columnconfigure(1, weight=1) buttons = UtilityButtonList(master, closeText='Ok', closeCmd=self.updateParams, helpUrl=self.help_url) buttons.grid(row=row, column=0, sticky='ew') def loadValues(self): directory = self.parent.fileSelect.getDirectory() fileSelectPopup = FileSelectPopup(self, title='Select Sampled Data File', dismiss_text='Cancel', selected_file_must_exist=True, multiSelect=False, directory=directory) fileName = fileSelectPopup.file_select.getFile() fileObj = open(fileName, 'rU') data = '' line = fileObj.readline() while line: data += line line = fileObj.readline() data = re.sub(',\s+', ',', data) data = re.sub('\s+', ',', data) data = re.sub(',,', ',', data) data = re.sub('[^0-9,.\-+eE]', '', data) self.valueEntry.set(data) def copyValues(self): expt = self.pseudoList.getObject() if expt: dataDim = getExperimentSampledDim(expt) values = dataDim.pointValues self.nptsEntry.set(len(values)) self.valueEntry.set(values) def updateParams(self): params = self.params if self.pseudoButton.get() == self.pseudoEntries[0]: npts = self.nptsEntry.get() params.npts[self.dim] = npts values = self.valueEntry.get() try: params.setSampledDim(self.dim, values) except ApiError, e: showError('Set Sampled Dim', e.error_msg, parent=self) return else:
class 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 CalcCouplingPopup(BasePopup): def __init__(self, parent, *args, **kw): self.waiting = False self.peakList = None self.peakLists = [] self.windowPane = None self.cluster = None self.clusters = {} self.multiplet = None self.guiParent = parent BasePopup.__init__(self, parent=parent, title='Measure Couplings', **kw) self.updateWindows() self.updateAfter() def body(self, guiFrame): guiFrame.grid_columnconfigure(0, weight=1) row = 0 frame = LabelFrame(guiFrame, text='Options') frame.grid(row=row, column=0, sticky='ew') frame.grid_columnconfigure(1, weight=1) frame.grid_rowconfigure(1, weight=1) label = Label(frame, text='Window:') label.grid(row=0, column=0, sticky='nw') self.windowPulldown = PulldownList(frame, callback=self.changeWindow) self.windowPulldown.grid(row=0, column=1, sticky='nw') label = Label(frame, text='Multiplet Pattern:') label.grid(row=1, column=0, columnspan=2, sticky='nw') self.multipletButtons = PartitionedSelector(frame, callback=self.setMultiplet, radio=True) self.multipletButtons.grid(row=2, column=0, columnspan=2, sticky='ew') row += 1 frame = LabelFrame(guiFrame, text='Active Peak Lists') frame.grid(row=row, column=0, sticky='ew') frame.grid_columnconfigure(0, weight=1) frame.grid_rowconfigure(0, weight=1) headingList = [ 'Experiment', 'Spectrum', 'List', 'Coupled Dims', 'Experiment Type' ] self.peakListMatrix = ScrolledMatrix(frame, headingList=headingList, callback=None, multiSelect=False) self.peakListMatrix.grid(row=0, column=0, sticky='nsew') row += 1 guiFrame.grid_rowconfigure(row, weight=1) frame = LabelFrame(guiFrame, text='Multiplet Peak Clusters') frame.grid(row=row, column=0, sticky='nsew') frame.grid_columnconfigure(0, weight=1) frame.grid_rowconfigure(0, weight=1) headingList = [ '#', 'Main\nAssignment', 'Num\nPeaks', 'Coupling\nAssignment', 'Value', 'Value' ] self.clusterMatrix = ScrolledMatrix(frame, headingList=headingList, callback=self.selectCluster, multiSelect=True) self.clusterMatrix.grid(row=0, column=0, sticky='nsew') row += 1 texts = [ 'Cluster Selected\nPeaks', 'Assign\nCouplings', 'List\nPeaks', 'Find\nPeaks', 'Delete\nClusters' ] commands = [ self.clusterSelectedPeaks, self.assignCouplings, self.showPeaks, self.findPeaks, self.deleteClusters ] self.bottomButtons = UtilityButtonList(guiFrame, texts=texts, expands=True, commands=commands, helpUrl=self.help_url) self.bottomButtons.grid(row=row, column=0, sticky='ew') self.administerNotifiers(self.registerNotify) def administerNotifiers(self, notifyFunc): for clazz in ('ccp.nmr.Nmr.DataSource', 'ccp.nmr.Nmr.Experiment'): notifyFunc(self.updatePeakListsAfter, clazz, 'setName') for func in ('__init__', 'delete', 'setName'): notifyFunc(self.updateWindows, 'ccpnmr.Analysis.SpectrumWindow', func) for func in ('__init__', 'delete'): notifyFunc(self.updatePeakListsAfter, 'ccp.nmr.Nmr.PeakList', func) for func in ('__init__', 'delete', 'addPeak', 'setPeaks', 'removePeak', 'setAnnotation'): notifyFunc(self.updateAfter, 'ccp.nmr.Nmr.PeakCluster', func) def deleteClusters(self): clusters = self.clusterMatrix.currentObjects for cluster in clusters: deleteCluster(cluster) def findPeaks(self): if self.windowPane and self.cluster: peaks = list(self.cluster.peaks) if not peaks: return spectrum = peaks[0].peakList.dataSource analysisSpectrum = spectrum.analysisSpectrum view = self.windowPane.findFirstSpectrumWindowView( analysisSpectrum=analysisSpectrum) windowFrame = self.windowPane.getWindowFrame() position = {} n = float(len(peaks)) for axisMapping in view.axisMappings: dim = axisMapping.analysisDataDim.dataDim.dim xyz = axisMapping.label mean = 0.0 for peak in peaks: peakDim = peak.findFirstPeakDim(dim=dim) mean += peakDim.realValue mean /= n position[xyz] = mean windowFrame.gotoPosition(position) def updatePeakListsAfter(self, object): if object.className == 'Experiment': for spectrum in object.dataSources: for peakList in spectrum.peakLists: if peakList in self.peakLists: self.updatePeakLists() elif object.className == 'DataSource': for peakList in object.peakLists: if peakList in self.peakLists: self.updatePeakLists() else: if object in self.peakLists: self.updatePeakLists(object) def open(self): BasePopup.open(self) self.updateWindows() self.updateAfter() def showPeaks(self): peaks = {} for peakCluster in self.clusterMatrix.currentObjects: for peak in peakCluster.peaks: peaks[peak] = True if peaks: self.parent.viewPeaks(peaks.keys()) def getWindows(self): windowPanes = [] for window in self.analysisProject.sortedSpectrumWindows(): for windowPane in window.sortedSpectrumWindowPanes(): for view in windowPane.spectrumWindowViews: if view.isPosVisible or view.isNegVisible: spectrum = view.analysisSpectrum.dataSource if self.getCoupledExpDims(spectrum.experiment): windowPanes.append(windowPane) break return windowPanes def getCoupledExpDims(self, experiment): """ Descrn: List the dimensions (ExpDims) of an experiment which carry couplings. Inputs: Nmr.Experiment Output: List of Nmr.ExpDims """ COUPLING_TYPES = ('JCoupling', 'Rdc', 'DipolarCoupling') expDims = [] refExperiment = experiment.refExperiment if refExperiment: for expDim in experiment.expDims: refExpDim = expDim.refExpDim if not refExpDim: # Could be sampled dim continue for refExpDimRef in refExpDim.refExpDimRefs: if refExpDimRef.coupledIsotopeCodes: expDims.append(expDim) break if not expDims: for expDim in experiment.expDims: for expDimRef in expDim.expDimRefs: if expDimRef.measurementType in COUPLING_TYPES: expDims.append(expDim) break return expDims def changeWindow(self, windowPane): if windowPane is not self.windowPane: self.windowPane = windowPane self.updatePeakLists() def updateWindows(self, *object): panes = self.getWindows() index = 0 names = [] pane = self.windowPane if panes: names = [getWindowPaneName(wp) for wp in panes] if pane not in panes: pane = panes[0] index = panes.index(pane) if pane is not self.windowPane: self.windowPane = pane self.updatePeakLists() self.windowPulldown.setup(names, panes, index) def getPeakLists(self): peakLists = [] if self.windowPane: for view in self.windowPane.spectrumWindowViews: try: spectrum = view.analysisSpectrum.dataSource except: continue if spectrum.peakLists: peakList = spectrum.activePeakList if not peakList: peakList = spectrum.findFirstPeakList() peakLists.append(peakList) return peakLists def updatePeakLists(self, peakList=None): objectList = self.getPeakLists() textMatrix = [] spectra = {} for peakList0 in objectList: spectrum = peakList0.dataSource experiment = spectrum.experiment spectra[spectrum] = True refExperiment = experiment.refExperiment coupledDims = [] if refExperiment: experimentType = refExperiment.name for expDim in experiment.expDims: refExpDim = expDim.refExpDim isotopes = {} for refExpDimRef in refExpDim.refExpDimRefs: for isotope in refExpDimRef.coupledIsotopeCodes: isotopes[isotope] = True if isotopes: isoString = ','.join(isotopes) coupledDims.append('%d (%s)' % (expDim.dim, isoString)) else: experimentType = None if not coupledDims: coupledDims = None else: coupledDims = ' '.join(coupledDims) datum = [ experiment.name, spectrum.name, peakList0.serial, coupledDims, experimentType ] textMatrix.append(datum) self.peakLists = objectList self.peakListMatrix.update(objectList=objectList, textMatrix=textMatrix) self.multiplet = None for spectrum in spectra.keys(): multiplet = self.getSpectrumMultiplet(spectrum) if multiplet: self.multiplet = MULTIPLET_PEAK_DICT.get(multiplet) break self.updateMultipletButtons() self.updateAfter() def setMultiplet(self, pattern): for peakList in self.peakLists: spectrum = peakList.dataSource for name in MULTIPLET_PEAK_DICT.keys(): if MULTIPLET_PEAK_DICT[name] == pattern: setSpectrumMultipletPattern(spectrum, name) break self.multiplet = pattern def setSpectrumMultiplet(self, spectrum, pattern): prevPattern = getSpectrumMultipletPattern(spectrum) if prevPattern and (prevPattern != pattern): if showOkCancel('Query', 'Really change multiplet pattern?', parent=self): setSpectrumMultipletPattern(spectrum, pattern) def getSpectrumMultiplet(self, spectrum): name = getSpectrumMultipletPattern(spectrum) if not name: name = self.predictSpectrumMultiplet(spectrum) setSpectrumMultipletPattern(spectrum, name) return name def predictSpectrumMultiplet(self, spectrum): name = None refExperiment = spectrum.experiment.refExperiment if refExperiment: name = EXPT_MULTIPLETS.get(refExperiment.name) return name def updateMultipletButtons(self): import re labels = [] objects = [] colors = [] selected = None if self.windowPane: coupledAxes = [] spectra = {} for peakList in self.peakLists: spectra[peakList.dataSource] = True multiplet = self.getSpectrumMultiplet(peakList.dataSource) selected = MULTIPLET_PEAK_DICT[multiplet] for spectrum in spectra.keys(): mapping = getDataDimAxisMapping(spectrum, self.windowPane) for axisLabel in ('x', 'y'): dataDim = mapping[axisLabel] for expDimRef in dataDim.expDim.expDimRefs: if expDimRef.refExpDimRef and expDimRef.refExpDimRef.coupledIsotopes: if axisLabel not in coupledAxes: coupledAxes.append(axisLabel) break else: coupledAxes = ['x', 'y'] for name in MULTIPLET_PEAK_DICT.keys(): pattern = MULTIPLET_PEAK_DICT[name] if type(pattern[0]) in (type(()), type([])): if 'y' not in coupledAxes: continue if len(pattern[0]) > 1: # x & y if 'x' not in coupledAxes: continue else: # y only if 'x' in coupledAxes: continue else: # x only if 'x' not in coupledAxes: continue if 'y' in coupledAxes: continue label = re.sub('\|', '\n', name) objects.append(pattern) labels.append(label) colors.append('#8080FF') self.multipletButtons.update(objects=objects, colors=colors, labels=labels) self.multipletButtons.setSelected([ selected, ]) def selectPeakList(self, object, row, col): self.peakList = object self.updateButtons() def selectCluster(self, object, row, col): self.cluster = object self.updateButtons() def assignCouplings(self): for cluster in self.clusterMatrix.currentObjects: assignPrimaryClusterCoupling(cluster) def assignAllCouplings(self): for cluster in self.clusters.keys(): assignPrimaryClusterCoupling(cluster) def clusterSelectedPeaks(self): peaks0 = self.parent.currentPeaks peaks = [] for peak in peaks0: if peak.peakList in self.peakLists: peaks.append(peak) if not peaks: showWarning('Cluster Failure', 'No peaks selected from active peak lists', parent=self) return if not self.multiplet: showWarning('Cluster Failure', 'No multiplet pattern selected', parent=self) return cluster = makeMultipletPeakCluster(peaks, self.multiplet, self.windowPane) if cluster: self.clusterMatrix.selectObject(cluster) def getActiveClusterCouplings(self): clusters = {} if self.peakLists: dims = {} for peakList in self.peakLists: experiment = peakList.dataSource.experiment for expDim in getCoupledExpDims(experiment): dims[expDim.dim] = True dims = dims.keys() for cluster in getPeakListsPeakClusters(self.peakLists): values = [] for dim in dims: values.append(getClusterCoupling(cluster, dim)) # Allow Nones? clusters[cluster] = values self.clusters = clusters return clusters def updateButtons(self): pass def updateAfter(self, cluster=None): if self.waiting: return if cluster: for peak in cluster.peaks: if peak.peakList in self.peakLists: self.waiting = True self.after_idle(self.update) break else: self.waiting = True self.after_idle(self.update) def update(self): clusters = self.getActiveClusterCouplings() textMatrix = [] objectList = [] headingList = [ '#', 'Main\nAssignment', 'Num\nPeaks', 'Coupling\nAssignment' ] if clusters: cluster0 = clusters.keys()[0] i = 1 for value in clusters[cluster0]: headingList.append('F%d Value\n(Hz)' % i) i += 1 else: headingList.append('Value') for cluster in clusters.keys(): couplingAssign = ','.join(getCouplingAnnotations(cluster)) datum = [ cluster.serial, cluster.annotation, len(cluster.peaks), couplingAssign ] values = clusters[cluster] for value in values: datum.append(value) textMatrix.append(datum) objectList.append(cluster) self.clusterMatrix.update(headingList=headingList, textMatrix=textMatrix, objectList=objectList) self.updateButtons() self.waiting = False def destroy(self): self.administerNotifiers(self.unregisterNotify) BasePopup.destroy(self)
class CingFrame(NmrCalcRunFrame): def __init__(self, parent, application, *args, **kw): project = application.project self.nmrProject = nmrProject = application.nmrProject if project: calcStore = project.findFirstNmrCalcStore(name=APP_NAME, nmrProject=nmrProject) or \ project.newNmrCalcStore(name=APP_NAME, nmrProject=nmrProject) else: calcStore = None self.application = application self.residue = None self.structure = None self.serverCredentials = None self.iCingBaseUrl = DEFAULT_URL self.resultsUrl = None self.chain = None self.serverDone = False NmrCalcRunFrame.__init__(self, parent, project, calcStore, *args, **kw) # # # # # # New Structure Frame # # # # # self.structureTable.grid_forget() self.structureButtons.grid_forget() self.ensemblePulldown.grid_forget() self.modelButtons.grid_forget() self.modelPulldown.grid_forget() frame = self.inputTabs.frames[0] frame.grid_rowconfigure(0, weight=0) frame.grid_rowconfigure(1, weight=1) label = Label(frame, text='Ensemble: ', grid=(0, 0)) self.structurePulldown = PulldownList( frame, callback=self.changeStructure, grid=(0, 1), tipText='The structure ensemble coordinates to submit') tipTexts = [ 'Conformational model number', 'Whether analyse this model' ] headingList = ['Model', 'Use'] editWidgets = [None, None] editGetCallbacks = [None, self.toggleModel] editSetCallbacks = [ None, None, ] self.modelTable = ScrolledMatrix(frame, grid=(1, 0), gridSpan=(1, 2), callback=self.selectStructModel, multiSelect=True, tipTexts=tipTexts, editWidgets=editWidgets, initialRows=2, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, headingList=headingList) tipTexts = [ 'Activate the selected models so that they will be consedered in the analysis', 'Deactivate the selected models so that they will not be considered in the analysis' ] texts = ['Activate Selected', 'Inactivate Selected'] commands = [self.activateModels, self.disableModels] buttons = ButtonList(frame, texts=texts, commands=commands, grid=(2, 0), gridSpan=(1, 2), tipTexts=tipTexts) # # # # # # Submission frame # # # # # # tab = self.tabbedFrame.frames[1] tab.expandGrid(1, 0) frame = LabelFrame(tab, text='Server Job Submission', grid=(0, 0)) frame.expandGrid(None, 2) srow = 0 label = Label(frame, text='iCing URL:', grid=(srow, 0)) self.iCingBaseUrlPulldown = PulldownList( frame, texts=URLS, objects=URLS, index=0, grid=(srow, 1), tipText='Web location of iCING server to use') srow += 1 label = Label(frame, text='Results File:', grid=(srow, 0)) self.resultFileEntry = Entry( frame, bd=1, text='', grid=(srow, 1), width=50, tipText='Name of file to store compressed CING results in') self.setZipFileName() button = Button(frame, text='Choose File', bd=1, sticky='ew', command=self.chooseZipFile, grid=(srow, 2), tipText='Select file to overwrite with CING results') srow += 1 label = Label(frame, text='Results URL:', grid=(srow, 0)) self.resultUrlEntry = Entry( frame, bd=1, text='', grid=(srow, 1), width=50, tipText='Web location where CING results will be posted') button = Button(frame, text='View Results HTML', bd=1, sticky='ew', command=self.viewHtmlResults, grid=(srow, 2), tipText='Open the HTML CING results in a web browser') srow += 1 tipTexts = [ 'Submit the CCPN project to the CING server', 'Determin whether the iCING job is complete, pending or has failed', 'Remove all trace of the last submissionfrom the iCING server', 'Download the compressed CING results, including HTML' ] texts = [ 'Submit Project!', 'Check Run Status', 'Purge Server Result', 'Download Results' ] commands = [ self.runCingServer, self.checkStatus, self.purgeCingServer, self.downloadResults ] self.buttonBar = ButtonList(frame, texts=texts, commands=commands, grid=(srow, 0), gridSpan=(1, 3), tipTexts=tipTexts) for button in self.buttonBar.buttons[:1]: button.config(bg=CING_BLUE) # # # # # # Residue frame # # # # # # frame = LabelFrame(tab, text='Residue Options', grid=(1, 0)) frame.expandGrid(1, 1) label = Label(frame, text='Chain: ') label.grid(row=0, column=0, sticky='w') self.chainPulldown = PulldownList( frame, callback=self.changeChain, tipText='Select the molecular system chain to consider') self.chainPulldown.grid(row=0, column=1, sticky='w') headingList = ['#', 'Residue', 'Linking', 'Decriptor', 'Use?'] tipTexts = [ 'Sequence number', 'Residue type code', 'In-chain connectivity of residue', 'Protonation and steriochemical state', 'Whether to consider the residue in the analysis' ] editWidgets = [None, None, None, None, None] editGetCallbacks = [None, None, None, None, self.toggleResidue] editSetCallbacks = [ None, None, None, None, None, ] self.residueMatrix = ScrolledMatrix(frame, headingList=headingList, multiSelect=True, tipTexts=tipTexts, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, callback=self.selectResidue) self.residueMatrix.grid(row=1, column=0, columnspan=2, sticky='nsew') tipTexts = [ 'Use the selected residues in the analysis', 'Do not use the selected residues in the analysis' ] texts = ['Activate Selected', 'Inactivate Selected'] commands = [self.activateResidues, self.deactivateResidues] self.resButtons = ButtonList(frame, texts=texts, commands=commands, tipTexts=tipTexts) self.resButtons.grid(row=2, column=0, columnspan=2, sticky='ew') """ # # # # # # Validate frame # # # # # # frame = LabelFrame(tab, text='Validation Options', grid=(2,0)) frame.expandGrid(None,2) srow = 0 self.selectCheckAssign = CheckButton(frame) self.selectCheckAssign.grid(row=srow, column=0,sticky='nw' ) self.selectCheckAssign.set(True) label = Label(frame, text='Assignments and shifts') label.grid(row=srow,column=1,sticky='nw') srow += 1 self.selectCheckResraint = CheckButton(frame) self.selectCheckResraint.grid(row=srow, column=0,sticky='nw' ) self.selectCheckResraint.set(True) label = Label(frame, text='Restraints') label.grid(row=srow,column=1,sticky='nw') srow += 1 self.selectCheckQueen = CheckButton(frame) self.selectCheckQueen.grid(row=srow, column=0,sticky='nw' ) self.selectCheckQueen.set(False) label = Label(frame, text='QUEEN') label.grid(row=srow,column=1,sticky='nw') srow += 1 self.selectCheckScript = CheckButton(frame) self.selectCheckScript.grid(row=srow, column=0,sticky='nw' ) self.selectCheckScript.set(False) label = Label(frame, text='User Python script\n(overriding option)') label.grid(row=srow,column=1,sticky='nw') self.validScriptEntry = Entry(frame, bd=1, text='') self.validScriptEntry.grid(row=srow,column=2,sticky='ew') scriptButton = Button(frame, bd=1, command=self.chooseValidScript, text='Browse') scriptButton.grid(row=srow,column=3,sticky='ew') """ # # # # # # # # # # self.update(calcStore) self.administerNotifiers(application.registerNotify) def downloadResults(self): if not self.run: msg = 'No current iCing run' showWarning('Failure', msg, parent=self) return credentials = self.serverCredentials if not credentials: msg = 'No current iCing server job' showWarning('Failure', msg, parent=self) return fileName = self.resultFileEntry.get() if not fileName: msg = 'No save file specified' showWarning('Failure', msg, parent=self) return if os.path.exists(fileName): msg = 'File %s already exists. Overwite?' % fileName if not showOkCancel('Query', msg, parent=self): return url = self.iCingBaseUrl iCingUrl = self.getServerUrl(url) logText = iCingRobot.iCingFetch(credentials, url, iCingUrl, fileName) print logText msg = 'Results saved to file %s\n' % fileName msg += 'Purge results from iCing server?' if showYesNo('Query', msg, parent=self): self.purgeCingServer() def getServerUrl(self, baseUrl): iCingUrl = os.path.join(baseUrl, 'icing/icing/serv/iCingServlet') return iCingUrl def viewHtmlResults(self): resultsUrl = self.resultsUrl if not resultsUrl: msg = 'No current iCing results URL' showWarning('Failure', msg, parent=self) return webBrowser = WebBrowser(self.application, popup=self.application) webBrowser.open(self.resultsUrl) def runCingServer(self): if not self.project: return run = self.run if not run: msg = 'No CING run setup' showWarning('Failure', msg, parent=self) return structureData = self.getStructureData() if not structureData: msg = 'No structure ensemble selected' showWarning('Failure', msg, parent=self) return if not structureData.models: msg = 'No structural models selected from ensemble' showWarning('Failure', msg, parent=self) return residueData = self.getResidueData() if not (residueData and residueData.residues): msg = 'No active residues selected in structure' showWarning('Failure', msg, parent=self) return url = self.iCingBaseUrlPulldown.getObject() url.strip() if not url: msg = 'No iCing server URL specified' showWarning('Failure', msg, parent=self) self.iCingBaseUrl = None return msg = 'Submit job now? You will be informed when the job is done.' if not showOkCancel('Confirm', msg, parent=self): return self.run.status = 'pending' self.iCingBaseUrl = url iCingUrl = self.getServerUrl(url) self.serverCredentials, results, tarFileName = iCingRobot.iCingSetup( self.project, userId='ccpnAp', url=iCingUrl) if not results: # Message already issued on failure self.run.status = 'failed' self.serverCredentials = None self.resultsUrl = None self.update() return else: credentials = self.serverCredentials os.unlink(tarFileName) entryId = iCingRobot.iCingProjectName(credentials, iCingUrl).get( iCingRobot.RESPONSE_RESULT) baseUrl, htmlUrl, logUrl, zipUrl = iCingRobot.getResultUrls( credentials, entryId, url) self.resultsUrl = htmlUrl # Save server data in this run for persistence setRunParameter(run, iCingRobot.FORM_USER_ID, self.serverCredentials[0][1]) setRunParameter(run, iCingRobot.FORM_ACCESS_KEY, self.serverCredentials[1][1]) setRunParameter(run, ICING_BASE_URL, url) setRunParameter(run, HTML_RESULTS_URL, htmlUrl) self.update() #run.inputStructures = structure.sortedModels() # select residues from the structure's chain #iCingRobot.iCingResidueSelection(credentials, iCingUrl, residueText) # Select models from ensemble #iCingRobot.iCingEnsembleSelection(credentials, iCingUrl, ensembleText) # Start the actual run self.serverDone = False iCingRobot.iCingRun(credentials, iCingUrl) # Fetch server progress occasionally, report when done # this function will call itself again and again self.after(CHECK_INTERVAL, self.timedCheckStatus) self.update() def timedCheckStatus(self): if not self.serverCredentials: return if self.serverDone: return status = iCingRobot.iCingStatus(self.serverCredentials, self.getServerUrl(self.iCingBaseUrl)) if not status: #something broke, already warned self.run.status = 'failed' return result = status.get(iCingRobot.RESPONSE_RESULT) if result == iCingRobot.RESPONSE_DONE: self.serverDone = True self.run.status = 'completed' msg = 'CING run is complete!' showInfo('Completion', msg, parent=self) return self.after(CHECK_INTERVAL, self.timedCheckStatus) def checkStatus(self): if not self.serverCredentials: return status = iCingRobot.iCingStatus(self.serverCredentials, self.getServerUrl(self.iCingBaseUrl)) if not status: #something broke, already warned return result = status.get(iCingRobot.RESPONSE_RESULT) if result == iCingRobot.RESPONSE_DONE: msg = 'CING run is complete!' showInfo('Completion', msg, parent=self) self.serverDone = True return else: msg = 'CING job is not done.' showInfo('Processing', msg, parent=self) self.serverDone = False return def purgeCingServer(self): if not self.project: return if not self.run: msg = 'No CING run setup' showWarning('Failure', msg, parent=self) return if not self.serverCredentials: msg = 'No current iCing server job' showWarning('Failure', msg, parent=self) return url = self.iCingBaseUrl results = iCingRobot.iCingPurge(self.serverCredentials, self.getServerUrl(url)) if results: showInfo('Info', 'iCing server results cleared') self.serverCredentials = None self.iCingBaseUrl = None self.serverDone = False deleteRunParameter(self.run, iCingRobot.FORM_USER_ID) deleteRunParameter(self.run, iCingRobot.FORM_ACCESS_KEY) deleteRunParameter(self.run, HTML_RESULTS_URL) else: showInfo('Info', 'Purge failed') self.update() def chooseZipFile(self): fileTypes = [ FileType('Zip', ['*.zip']), ] popup = FileSelectPopup(self, file_types=fileTypes, file=self.resultFileEntry.get(), title='Results zip file location', dismiss_text='Cancel', selected_file_must_exist=False) fileName = popup.getFile() if fileName: self.resultFileEntry.set(fileName) popup.destroy() def setZipFileName(self): if self.project: zipFile = '%s_CING_report.zip' % self.project.name self.resultFileEntry.set(zipFile) else: self.resultFileEntry.set('CING_report.zip') def selectStructModel(self, model, row, col): self.model = model def selectResidue(self, residue, row, col): self.residue = residue def getResidueData(self): chain = self.chain.chain chainCode = chain.code msCode = chain.molSystem.code dataObj = self.run.findFirstData(className=RESIDUE_DATA, ioRole='input', molSystemCode=msCode, chainCode=chainCode, name=APP_NAME) if not dataObj: dataObj = self.run.newMolResidueData(molSystemCode=msCode, chainCode=chainCode, ioRole='input', name=APP_NAME) # if not dataObj.residueSeqIds: seqIds = [r.seqId for r in self.chain.sortedResidues()] dataObj.residueSeqIds = seqIds return dataObj def getStructureData(self): eId = self.structure.ensembleId msCode = self.structure.molSystem.code dataObj = self.run.findFirstData(className=STRUCTURE_DATA, molSystemCode=msCode, ensembleId=eId, ioRole='input', name=APP_NAME) if not dataObj: dataObj = self.run.newStructureEnsembleData(ioRole='input', molSystemCode=msCode, ensembleId=eId, name=APP_NAME) # if not dataObj.modelSerials: serials = [m.serial for m in self.structure.sortedModels()] dataObj.modelSerials = serials for dataObjB in self.run.findAllData(className=STRUCTURE_DATA, name=APP_NAME, ioRole='input'): if dataObjB is not dataObj: dataObjB.delete() return dataObj def deactivateResidues(self): if self.run and self.chain: dataObj = self.getResidueData() seqIds = set(dataObj.residueSeqIds) for residue in self.residueMatrix.currentObjects: seqId = residue.seqId if seqId in seqIds: seqIds.remove(seqId) seqIds = list(seqIds) seqIds.sort() dataObj.residueSeqIds = seqIds self.updateResidues() def activateResidues(self): if self.run and self.chain: for residue in self.residueMatrix.currentObjects: dataObj = self.getResidueData() seqIds = set(dataObj.residueSeqIds) for residue in self.residueMatrix.currentObjects: seqId = residue.seqId if seqId not in seqIds: seqIds.add(seqId) seqIds = list(seqIds) seqIds.sort() dataObj.residueSeqIds = seqIds self.updateResidues() def activateModels(self): if self.run and self.structure: dataObj = self.getStructureData() serials = set(dataObj.modelSerials) for model in self.modelTable.currentObjects: serial = model.serial if serial not in serials: serials.add(serial) serials = list(serials) serials.sort() dataObj.modelSerials = serials self.updateModels() def disableModels(self): if self.run and self.structure: dataObj = self.getStructureData() serials = set(dataObj.modelSerials) for model in self.modelTable.currentObjects: serial = model.serial if serial in serials: serials.remove(serial) serials = list(serials) serials.sort() dataObj.modelSerials = serials self.updateModels() def toggleModel(self, *opt): if self.model and self.run and self.structure: dataObj = self.getStructureData() serials = set(dataObj.modelSerials) serial = self.model.serial if serial in serials: serials.remove(serial) else: serials.add(serial) serials = list(serials) serials.sort() dataObj.modelSerials = serials self.updateModels() def toggleResidue(self, *opt): if self.residue and self.run: dataObj = self.getResidueData() seqIds = set(dataObj.residueSeqIds) seqId = self.residue.seqId if seqId in seqIds: seqIds.remove(seqId) else: seqIds.add(seqId) seqIds = list(seqIds) seqIds.sort() dataObj.residueSeqIds = seqIds self.updateResidues() def updateResidues(self): if self.residue and (self.residue.topObject is not self.structure): self.residue = None textMatrix = [] objectList = [] colorMatrix = [] if self.chain: resDataObj = self.getResidueData() selectedRes = set(resDataObj.residues) chainCode = self.chain.code for residue in self.chain.sortedResidues(): msResidue = residue.residue if msResidue in selectedRes: colors = [None, None, None, None, CING_BLUE] use = 'Yes' else: colors = [None, None, None, None, None] use = 'No' datum = [ residue.seqCode, msResidue.ccpCode, msResidue.linking, msResidue.descriptor, use, ] textMatrix.append(datum) objectList.append(residue) colorMatrix.append(colors) self.residueMatrix.update(objectList=objectList, textMatrix=textMatrix, colorMatrix=colorMatrix) def updateChains(self): index = 0 names = [] chains = [] chain = self.chain if self.structure: chains = self.structure.sortedCoordChains() names = [chain.code for chain in chains] if chains: if chain not in chains: chain = chains[0] index = chains.index(chain) self.changeChain(chain) self.chainPulldown.setup(names, chains, index) def updateStructures(self): index = 0 names = [] structures = [] structure = self.structure if self.run: structures0 = [(s.ensembleId, s) for s in self.project.structureEnsembles] structures0.sort() for eId, structure0 in structures0: name = '%s:%s' % (structure0.molSystem.code, eId) structures.append(structure0) names.append(name) if structures: if structure not in structures: structure = structures[-1] index = structures.index(structure) if self.structure is not structure: self.changeStructure(structure) self.structurePulldown.setup(names, structures, index) def updateModels(self, obj=None): textMatrix = [] objectList = [] colorMatrix = [] if self.structure and self.run: strucDataObj = self.getStructureData() selectedSerials = set(strucDataObj.modelSerials) for model in self.structure.sortedModels(): if model.serial in selectedSerials: colors = [None, CING_BLUE] use = 'Yes' else: colors = [None, None] use = 'No' datum = [model.serial, use] textMatrix.append(datum) objectList.append(model) colorMatrix.append(colors) self.modelTable.update(objectList=objectList, textMatrix=textMatrix, colorMatrix=colorMatrix) def changeStructure(self, structure): if self.project and (self.structure is not structure): self.project.currentEstructureEnsemble = structure self.structure = structure if self.run: dataObj = self.getStructureData() serials = set(dataObj.modelSerials) serials2 = set([m.serial for m in structure.models]) serials = list(serials & serials2) serials.sort() dataObj.modelSerials = serials dataObj.ensembleId = structure.ensembleId dataObj.molSystemCode = structure.molSystem.code # Could clean up residue data if required # prob OK to have haning around in case structure # changes back #for dataObj in self.run.findAllData(className=RESIDUE_DATA, # ioRole='input', # molSystemCode=msCode, # name=APP_NAME) # dataObj.delete() self.updateModels() self.updateChains() def changeChain(self, chain): if self.project and (self.chain is not chain): self.chain = chain self.updateResidues() #def chooseValidScript(self): # # # Prepend default Cyana file extension below # fileTypes = [ FileType('Python', ['*.py']), ] # popup = FileSelectPopup(self, file_types = fileTypes, # title='Python file', dismiss_text='Cancel', # selected_file_must_exist = True) # fileName = popup.getFile() # self.validScriptEntry.set(fileName) # popup.destroy() def updateAll(self, project=None): if project: self.project = project self.nmrProject = nmrProject = project.currentNmrProject calcStore = project.findFirstNmrCalcStore(name='CING', nmrProject=nmrProject) or \ project.newNmrCalcStore(name='CING', nmrProject=nmrProject) else: calcStore = None if not self.project: return self.setZipFileName() if not self.project.currentNmrProject: name = self.project.name self.nmrProject = self.project.newNmrProject(name=name) else: self.nmrProject = self.project.currentNmrProject self.update(calcStore) def update(self, calcStore=None): NmrCalcRunFrame.update(self, calcStore) run = self.run urls = URLS index = 0 if run: userId = getRunTextParameter(run, iCingRobot.FORM_USER_ID) accessKey = getRunTextParameter(run, iCingRobot.FORM_ACCESS_KEY) if userId and accessKey: self.serverCredentials = [(iCingRobot.FORM_USER_ID, userId), (iCingRobot.FORM_ACCESS_KEY, accessKey)] url = getRunTextParameter(run, ICING_BASE_URL) if url: htmlUrl = getRunTextParameter(run, HTML_RESULTS_URL) self.iCingBaseUrl = url self.resultsUrl = htmlUrl # May be None self.resultUrlEntry.set(self.resultsUrl) if self.iCingBaseUrl and self.iCingBaseUrl not in urls: index = len(urls) urls.append(self.iCingBaseUrl) self.iCingBaseUrlPulldown.setup(urls, urls, index) self.updateButtons() self.updateStructures() self.updateModels() self.updateChains() def updateButtons(self, event=None): buttons = self.buttonBar.buttons if self.project and self.run: buttons[0].enable() if self.resultsUrl and self.serverCredentials: buttons[1].enable() buttons[2].enable() buttons[3].enable() else: buttons[1].disable() buttons[2].disable() buttons[3].disable() else: buttons[0].disable() buttons[1].disable() buttons[2].disable() buttons[3].disable()
class PrintFrame(LabelFrame): def __init__(self, parent, getOption=None, setOption=None, text='Print Options', haveTicks=False, doOutlineBox=True, *args, **kw): self.getOption = getOption self.setOption = setOption self.haveTicks = haveTicks self.doOutlineBox = doOutlineBox LabelFrame.__init__(self, parent=parent, text=text, *args, **kw) self.file_select_popup = None self.getOptionValues() try: size_index = self.paper_types.index(self.paper_type) except: size_index = 0 try: other_unit_index = self.paper_units.index(self.other_unit) except: other_unit_index = 0 try: orientation_index = self.paper_orientations.index(self.orientation) except: orientation_index = 0 try: style_index = self.style_choices.index(self.output_style) except: style_index = 0 try: format_index = self.format_choices.index(self.output_format) except: format_index = 0 if haveTicks: try: tick_location_index = self.tick_locations.index( self.tick_location) except: tick_location_index = 0 self.grid_columnconfigure(2, weight=1) row = 0 label = Label(self, text='File:') label.grid(row=row, column=0, sticky='e') self.file_entry = Entry(self, width=40, text=self.file_name) self.file_entry.grid(row=row, column=1, columnspan=2, sticky='ew') button = Button(self, text='Choose File', command=self.findFile) button.grid(row=row, column=3, rowspan=2, sticky='nsew') row += 1 label = Label(self, text='Title:') label.grid(row=row, column=0, sticky='e') self.title_entry = Entry(self, width=40, text=self.title) self.title_entry.grid(row=row, column=1, columnspan=2, sticky='ew') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(4, weight=1) label = Label(frame, text='Paper size:') label.grid(row=0, column=0, sticky='e') entries = [] for t in paper_types: if t == Output.other_paper_type: entry = t else: (w, h, u) = paper_sizes[t] entry = t + ' (%2.1f %s x %2.1f %s)' % (w, u, h, u) entries.append(entry) self.size_menu = PulldownList(frame, callback=self.changedSize, texts=entries, index=size_index) self.size_menu.grid(row=0, column=1, sticky='w') self.other_frame = Frame(frame) self.other_frame.grid_columnconfigure(0, weight=1) self.other_entry = FloatEntry(self.other_frame, text=self.other_size, isArray=True) self.other_entry.grid(row=0, column=0, sticky='ew') self.other_unit_menu = PulldownList(self.other_frame, texts=paper_units, index=other_unit_index) self.other_unit_menu.grid(row=0, column=1, sticky='ew') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) frame.grid_columnconfigure(3, weight=1) frame.grid_columnconfigure(5, weight=1) label = Label(frame, text='Orientation:') label.grid(row=0, column=0, sticky='e') self.orientation_menu = PulldownList(frame, texts=paper_orientations, index=orientation_index) self.orientation_menu.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Style:') label.grid(row=0, column=2, sticky='e') self.style_menu = PulldownList(frame, texts=style_choices, index=style_index) self.style_menu.grid(row=0, column=3, sticky='w') label = Label(frame, text=' Format:') label.grid(row=0, column=4, sticky='e') self.format_menu = PulldownList(frame, callback=self.changedFormat, texts=format_choices, index=format_index) self.format_menu.grid(row=0, column=5, sticky='w') if haveTicks: row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) frame.grid_columnconfigure(3, weight=1) label = Label(frame, text='Tick Location:') label.grid(row=0, column=0, sticky='e') self.tick_menu = PulldownList(frame, texts=tick_locations, index=tick_location_index) self.tick_menu.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Tick Placement:') label.grid(row=0, column=2, sticky='e') self.tick_buttons = CheckButtons(frame, entries=tick_placements, selected=self.tick_placement) self.tick_buttons.grid(row=0, column=3, sticky='w') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(3, weight=1) label = Label(frame, text='Include:') label.grid(row=0, column=0, sticky='e') self.border_buttons = CheckButtons(frame, entries=border_decorations, selected=self.border_decoration) self.border_buttons.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Scaling:') label.grid(row=0, column=2, sticky='e') self.scaling_scale = Scale(frame, orient=Tkinter.HORIZONTAL, value=self.scaling) self.scaling_scale.grid(row=0, column=3, sticky='ew') def destroy(self): self.setOptionValues() if self.file_select_popup: self.file_select_popup.destroy() Frame.destroy(self) def getOptionValues(self): getOption = self.getOption if getOption: file_name = getOption('FileName', defaultValue='') title = getOption('Title', defaultValue='') paper_type = getOption('PaperSize', defaultValue=paper_types[0]) paper_type = paper_type_dict.get(paper_type, paper_types[0]) other_height = getOption('OtherHeight', defaultValue=10) other_width = getOption('OtherWidth', defaultValue=10) other_size = [other_height, other_width] other_unit = getOption('OtherUnit', defaultValue=paper_units[0]) orientation = getOption('Orientation', defaultValue=paper_orientations[0]) in_color = getOption('InColor', defaultValue=True) if in_color: output_style = style_choices[0] else: output_style = style_choices[1] format_option = getOption('OutputFormat', defaultValue=format_options[0]) output_format = format_choices[format_options.index(format_option)] if self.haveTicks: tick_outside = getOption('TickOutside', defaultValue=tick_locations[0]) if tick_outside: tick_location = tick_locations.index(PrintTicks.Outside) else: tick_location = tick_locations.index(PrintTicks.Inside) tick_placement = getTickPlacement1( getOption('TickPlacement', defaultValue='nsew')) dateTime = getOption('ShowsDateTime', defaultValue=True) fileName = getOption('ShowsFileName', defaultValue=True) border_decoration = [] if dateTime: border_decoration.append(border_decorations[0]) if fileName: border_decoration.append(border_decorations[1]) scaling = getOption('Scaling', defaultValue=0.9) scaling = int(round(100.0 * scaling)) else: file_name = '' title = '' paper_type = paper_types[0] other_unit = paper_units[0] other_size = '' orientation = paper_orientations[0] output_style = style_choices[0] output_format = format_choices[0] if self.haveTicks: tick_location = tick_locations[0] tick_placement = tick_placements border_decoration = border_decorations scaling = 90 if not self.haveTicks: tick_location = None tick_placement = None self.file_name = file_name self.title = title self.paper_type = paper_type self.other_unit = other_unit self.other_size = other_size self.orientation = orientation self.output_style = output_style self.output_format = output_format self.tick_location = tick_location self.tick_placement = tick_placement self.border_decoration = border_decoration self.scaling = scaling def setOptionValues(self): self.file_name = file_name = self.file_entry.get() self.title = title = self.title_entry.get() n = self.size_menu.getSelectedIndex() self.paper_type = paper_type = paper_types[n] if paper_type == Output.other_paper_type: other_size = self.other_entry.get() other_unit = self.other_unit_menu.getText() else: other_size = None other_unit = None self.other_size = other_size self.other_unit = other_unit self.paper_orientation = paper_orientation = self.orientation_menu.getText( ) self.output_style = output_style = self.style_menu.getText() self.output_format = output_format = self.format_menu.getText() if self.haveTicks: tick_location = self.tick_menu.getText() tick_placement = self.tick_buttons.getSelected() else: tick_location = tick_placement = None self.tick_location = tick_location self.tick_placement = tick_placement self.border_decoration = border_decoration = self.border_buttons.getSelected( ) scaling = self.scaling_scale.get() self.scaling = scaling = int(round(scaling)) setOption = self.setOption if setOption: setOption('FileName', value=file_name) setOption('Title', value=title) if paper_type == Output.other_paper_type: setOption('OtherHeight', value=other_size[0]) setOption('OtherWidth', value=other_size[1]) setOption('OtherUnit', value=other_unit) else: paper_type = paper_type_inverse_dict[paper_type] setOption('PaperSize', value=paper_type) setOption('Orientation', value=paper_orientation) in_color = (output_style == style_choices[0]) setOption('InColor', value=in_color) output_format = format_options[format_choices.index(output_format)] setOption('OutputFormat', value=output_format) if self.haveTicks: tick_outside = (tick_location == PrintTicks.Outside) setOption('TickOutside', value=tick_outside) tick_placement = getTickPlacement2(tick_placement) setOption('TickPlacement', value=tick_placement) dateTime = (border_decorations[0] in border_decoration) fileName = (border_decorations[1] in border_decoration) setOption('ShowsDateTime', value=dateTime) setOption('ShowsFileName', value=fileName) setOption('Scaling', value=0.01 * scaling) def findFile(self): if self.file_select_popup: self.file_select_popup.open() else: file_types = [ FileType('All', ['*']), FileType('PostScript', ['*.ps', '*.eps']), FileType('PDF', ['*.pdf', '*.ai']) ] self.file_select_popup = FileSelectPopup(self, file_types=file_types) file = self.file_select_popup.getFile() if file: self.file_entry.set(file) def changedSize(self, entry): if entry == Output.other_paper_type: self.other_frame.grid(row=0, column=2, columnspan=2, sticky='w') else: self.other_frame.grid_forget() def changedFormat(self, entry): file_suffix = file_suffixes.get(entry) if not file_suffix: return file_name = self.file_entry.get() if not file_name: return for suffix in format_suffixes: if file_name.endswith(suffix): if suffix != file_suffix: n = len(suffix) file_name = file_name[:-n] + file_suffix self.file_entry.set(file_name) break else: file_name = file_name + file_suffix self.file_entry.set(file_name) # width and height are of plot, in pixels def getOutputHandler(self, width, height, fonts=None): if not fonts: fonts = [] else: fonts = list(fonts) for n in range(len(fonts)): if fonts[n] == 'Times': fonts[n] = 'Times-Roman' self.setOptionValues() if not self.file_name: showError('No file', 'No file specified', parent=self) return None if os.path.exists(self.file_name): if not showYesNo('File exists', 'File "%s" exists, overwrite?' % self.file_name, parent=self): return None if (self.paper_type == Output.other_paper_type): paper_size = self.other_size + [self.other_unit] else: paper_size = paper_sizes[self.paper_type] output_scaling = self.scaling / 100.0 font = 'Times-Roman' border_text = {} for decoration in self.border_decoration: if (decoration == 'Time & date'): location = 'se' text = time.ctime(time.time()) elif (decoration == 'File name'): location = 'sw' text = self.file_name else: continue # should not be here border_text[location] = (text, font, 12) if (self.title): location = 'n' border_text[location] = (self.title, font, 18) if font not in fonts: fonts.append(font) outputHandler = PrintHandler.getOutputHandler( self.file_name, width, height, output_scaling=output_scaling, paper_size=paper_size, paper_orientation=self.paper_orientation, output_style=self.output_style, output_format=self.output_format, border_text=border_text, fonts=fonts, do_outline_box=self.doOutlineBox) return outputHandler def getAspectRatio(self): self.setOptionValues() if self.paper_type == Output.other_paper_type: paper_size = self.other_size else: paper_size = paper_sizes[self.paper_type] r = paper_size[1] / paper_size[0] if self.paper_orientation == 'Landscape': r = 1.0 / r return r
class PrintFrame(Frame): def __init__(self, parent, getOption = None, setOption = None, haveTicks = False, doOutlineBox = True, *args, **kw): self.getOption = getOption self.setOption = setOption self.haveTicks = haveTicks self.doOutlineBox = doOutlineBox Frame.__init__(self, parent=parent, *args, **kw) self.file_select_popup = None self.getOptionValues() try: size_index = paper_types.index(self.paper_type) except: size_index = 0 try: other_unit_index = paper_units.index(self.other_unit) except: other_unit_index = 0 try: orientation_index = paper_orientations.index(self.paper_orientation) except: orientation_index = 0 try: style_index = style_choices.index(self.output_style) except: style_index = 0 try: format_index = format_choices.index(self.output_format) except: format_index = 0 if haveTicks: try: tick_location_index = tick_locations.index(self.tick_location) except: tick_location_index = 0 self.grid_columnconfigure(1, weight=1) row = 0 button = Button(self, text='File:', command=self.findFile, tipText='Select location to save print file') button.grid(row=row, column=0, sticky='e') self.file_entry = Entry(self, width=40, text=self.file_name, tipText='Location where file is saved on disk') self.file_entry.grid(row=row, column=1, sticky='ew') row += 1 label = Label(self, text='Title:') label.grid(row=row, column=0, sticky='e') self.title_entry = Entry(self, width=40, text=self.title, tipText='Title of the printout, displayed at top') self.title_entry.grid(row=row, column=1, sticky='ew') row += 1 label = Label(self, text='X axis label:') label.grid(row=row, column=0, sticky='e') self.x_axis_entry = Entry(self, width=40, text=self.x_axis_label, tipText='X axis label for the printout') self.x_axis_entry.grid(row=row, column=1, sticky='ew') row += 1 label = Label(self, text='Y axis label:') label.grid(row=row, column=0, sticky='e') self.y_axis_entry = Entry(self, width=40, text=self.y_axis_label, tipText='Y axis label for the printout') self.y_axis_entry.grid(row=row, column=1, sticky='ew') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=2, sticky='ew') frame.grid_columnconfigure(4, weight=1) label = Label(frame, text='Paper size:') label.grid(row=0, column=0, sticky='e') entries = [] for t in paper_types: if t == Output.other_paper_type: entry = t else: (w, h, u) = paper_sizes[t] entry = t + ' (%2.1f %s x %2.1f %s)' % (w, u, h, u) entries.append(entry) self.size_menu = PulldownList(frame, callback=self.changedSize, texts=entries, index=size_index, tipText='The paper size for the printout') self.size_menu.grid(row=0, column=1, sticky='w') self.other_frame = Frame(frame) self.other_frame.grid_columnconfigure(0, weight=1) self.other_entry = FloatEntry(self.other_frame, text=self.other_size, isArray=True, tipText='The size of the Other paper in both dimensions; this requires two values, space or comma separated') self.other_entry.grid(row=0, column=0, sticky='ew') self.other_unit_menu= PulldownList(self.other_frame, texts=paper_units, index=other_unit_index, tipText='The unit for the Other paper size') self.other_unit_menu.grid(row=0, column=1, sticky='ew') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) frame.grid_columnconfigure(3, weight=1) frame.grid_columnconfigure(5, weight=1) label = Label(frame, text='Orientation:') label.grid(row=0, column=0, sticky='e') self.orientation_menu = PulldownList(frame, texts=paper_orientations, index=orientation_index, tipText='Whether the paper should be set in Portrait or Landscape mode') self.orientation_menu.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Style:') label.grid(row=0, column=2, sticky='e') self.style_menu = PulldownList(frame, texts=style_choices, index=style_index, tipText='Whether the printout should be in colour or black and white') self.style_menu.grid(row=0, column=3, sticky='w') label = Label(frame, text=' Format:') label.grid(row=0, column=4, sticky='e') self.format_menu = PulldownList(frame, callback=self.changedFormat, texts=format_choices, index=format_index, tipText='Whether to save as PS, EPS or PDF') self.format_menu.grid(row=0, column=5, sticky='w') if haveTicks: row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) frame.grid_columnconfigure(3, weight=1) label = Label(frame, text='Tick Location:') label.grid(row=0, column=0, sticky='e') self.tick_menu = PulldownList(frame, texts=tick_locations, index=tick_location_index, tipText='Whether the tick marks appear on the inside or outside of the frame') self.tick_menu.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Tick Placement:') label.grid(row=0, column=2, sticky='e') if self.tick_placement is None: selected = None else: selected = [(x in self.tick_placement) for x in tick_placements] self.tick_buttons = CheckButtons(frame, entries=tick_placements, selected=selected, tipTexts=('Whether the tick marks appear on the top and/or bottom and/or left and/or right',)) self.tick_buttons.grid(row=0, column=3, sticky='w') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) frame.grid_columnconfigure(3, weight=1) label = Label(frame, text='Tick Font:') label.grid(row=0, column=0, sticky='e') self.tick_font_list = FontList(frame, mode='Print', selected=self.tick_font, extraTexts=[PrintTicks.no_tick_text], tipText='The font used for the tick mark labels') self.tick_font_list.grid(row=0, column=1, sticky='w') label = Label(frame, text='Tick Spacing:') label.grid(row=0, column=2, sticky='e') # TBD: put preferred choice in data model self.spacing_menu = PulldownList(frame, texts=spacing_choices, index=0, callback=self.changedSpacing, tipText='Whether the program should automatically calculate the major/minor tick spacings and how many decimal places are used for the ticks, or whether the these are specified manually') self.spacing_menu.grid(row=0, column=3, sticky='w') ff = self.spacing_frame = Frame(frame) ff.grid_columnconfigure(1, weight=1) ff.grid_columnconfigure(2, weight=1) label = Label(ff, text='Tick Spacing') label.grid(row=0, column=0, sticky='w') label = Label(ff, text='Major') label.grid(row=0, column=1, sticky='ew') label = Label(ff, text='Minor') label.grid(row=0, column=2, sticky='ew') label = Label(ff, text='Decimals') label.grid(row=0, column=3, sticky='ew') label = Label(ff, text='X:') label.grid(row=1, column=0, sticky='w') self.x_major_entry = FloatEntry(ff, tipText='The spacing in display units of the major tick marks in the X dimension') self.x_major_entry.grid(row=1, column=1, sticky='ew') self.x_minor_entry = FloatEntry(ff, tipText='The spacing in display units of the minor tick marks in the X dimension (not printed if left blank)') self.x_minor_entry.grid(row=1, column=2, sticky='ew') self.x_decimal_entry = IntEntry(ff, tipText='The number of decimal places for the tick numbers in the X dimension') self.x_decimal_entry.grid(row=1, column=3, sticky='ew') label = Label(ff, text='Y:') label.grid(row=2, column=0, sticky='w') self.y_major_entry = FloatEntry(ff, tipText='The spacing in display units of the major tick marks in the Y dimension') self.y_major_entry.grid(row=2, column=1, sticky='ew') self.y_minor_entry = FloatEntry(ff, tipText='The spacing in display units of the minor tick marks in the Y dimension (not printed if left blank)') self.y_minor_entry.grid(row=2, column=2, sticky='ew') self.y_decimal_entry = IntEntry(ff, tipText='The number of decimal places for the tick numbers in the Y dimension') self.y_decimal_entry.grid(row=2, column=3, sticky='ew') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) label = Label(frame, text='Tick Length:') label.grid(row=0, column=0, sticky='e') # TBD: put preferred choice in data model self.tick_length_menu = PulldownList(frame, texts=tick_length_choices, index=0, callback=self.changedLength, tipText='Whether the program should automatically calculate the major/minor tick lengths, or whether the these are specified manually') self.tick_length_menu.grid(row=0, column=1, sticky='w') ff = self.length_frame = Frame(frame) ff.grid_columnconfigure(1, weight=1) label = Label(ff, text=' Major length:') label.grid(row=0, column=0, sticky='w') self.length_major_entry = FloatEntry(ff, tipText='The length in points of the major tick marks') self.length_major_entry.grid(row=0, column=1, sticky='w') label = Label(ff, text='Minor length:') label.grid(row=0, column=2, sticky='w') self.length_minor_entry = FloatEntry(ff, tipText='The length in points of the minor tick marks') self.length_minor_entry.grid(row=0, column=3, sticky='w') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(3, weight=1) frame.grid_columnconfigure(4, weight=1) label = Label(frame, text='Scaling:') label.grid(row=0, column=0, sticky='e') # TBD: put preferred choice in data model self.scaling_menu = PulldownList(frame, texts=scaling_choices, index=0, callback=self.changedScaling, tipText='Whether the plot should be scaled as a percentage of the maximum size that would fit on the paper, or instead should be specified by the number of cms or inches per unit') self.scaling_menu.grid(row=0, column=1, sticky='ew') self.scaling_scale = Scale(frame, orient=Tkinter.HORIZONTAL, value=self.scaling, tipText='The percentage of the maximum size that would fit on the paper that the plot is scaled by') self.scaling_scale.grid(row=0, column=2, columnspan=3, sticky='ew') self.x_scaling_label = Label(frame, text='X:') self.x_scaling_entry = FloatEntry(frame, tipText='The scaling that should be used in the X dimension as cms or inches per unit') self.y_scaling_label = Label(frame, text='Y:') self.y_scaling_entry = FloatEntry(frame, tipText='The scaling that should be used in the Y dimension as cms or inches per unit') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='w') frame.grid_columnconfigure(2, weight=1) label = Label(frame, text='Include:') label.grid(row=0, column=0, sticky='e') tipTexts = ('Whether the time and date should be included in the printout', 'Whether the file name should be included in the printout') if self.border_decoration is None: selected = None else: selected = [(x in self.border_decoration) for x in border_decorations] self.border_buttons = CheckButtons(frame, entries=border_decorations, selected=selected, tipTexts=tipTexts) self.border_buttons.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Using Font:') label.grid(row=0, column=2, sticky='e') self.border_font_list = FontList(frame, mode='Print', selected=self.border_font, tipText='The font used for the border texts') self.border_font_list.grid(row=0, column=3, sticky='w') row += 1 label = Label(self, text='Line width:') label.grid(row=row, column=0, sticky='w') self.linewidth_entry = FloatEntry(self, width=10, text=self.linewidth, tipText='Line width for drawing') self.linewidth_entry.grid(row=row, column=1, sticky='w') def destroy(self): self.setOptionValues() if self.file_select_popup: self.file_select_popup.destroy() Frame.destroy(self) def getOptionValues(self): getOption = self.getOption if getOption: file_name = getOption('FileName', defaultValue='') title = getOption('Title', defaultValue='') x_axis_label = getOption('XAxisLabel', defaultValue='') y_axis_label = getOption('YAxisLabel', defaultValue='') paper_type = getOption('PaperSize', defaultValue=paper_types[0]) paper_type = paper_type_dict.get(paper_type, paper_types[0]) other_height = getOption('OtherHeight', defaultValue=10) other_width = getOption('OtherWidth', defaultValue=10) other_size = [other_height, other_width] other_unit = getOption('OtherUnit', defaultValue=paper_units[0]) paper_orientation = getOption('Orientation', defaultValue=paper_orientations[0]) in_color = getOption('InColor', defaultValue=True) if in_color: output_style = style_choices[0] else: output_style = style_choices[1] format_option = getOption('OutputFormat', defaultValue=format_options[0]) output_format = format_choices[format_options.index(format_option)] if self.haveTicks: tick_outside = getOption('TickOutside', defaultValue=tick_locations[0]) if tick_outside: tick_location = tick_locations.index(PrintTicks.Outside) else: tick_location = tick_locations.index(PrintTicks.Inside) tick_placement = getTickPlacement1(getOption('TickPlacement', defaultValue='nsew')) dateTime = getOption('ShowsDateTime', defaultValue=True) fileName = getOption('ShowsFileName', defaultValue=True) border_font = getOption('BorderFont', defaultValue='Helvetica 10') border_decoration = [] if dateTime: border_decoration.append(border_decorations[0]) if fileName: border_decoration.append(border_decorations[1]) if self.haveTicks: spacing_choice = getOption('SpacingChoice', defaultValue=spacing_choices[0]) x_major = getOption('XMajor', defaultValue=1.0) x_minor = getOption('XMinor', defaultValue=1.0) x_decimal = getOption('XDecimal', defaultValue=3) y_major = getOption('YMajor', defaultValue=1.0) y_minor = getOption('YMinor', defaultValue=1.0) y_decimal = getOption('YDecimal', defaultValue=3) tick_length_choice = getOption('TickLengthChoice', defaultValue=tick_length_choices[0]) tick_major = getOption('TickMajor', defaultValue=10) tick_minor = getOption('TickMinor', defaultValue=5) scaling_choice = getOption('ScalingChoice', defaultValue=scaling_choices[0]) scaling = getOption('Scaling', defaultValue=0.7) scaling = int(round(100.0 * scaling)) x_scaling = getOption('XScaling', defaultValue=1.0) y_scaling = getOption('YScaling', defaultValue=1.0) if self.haveTicks: tick_font = getOption('TickFont', defaultValue='Helvetica 10') linewidth = getOption('LineWidth', defaultValue=Output.default_linewidth) else: file_name = '' title = '' x_axis_label = '' y_axis_label = '' paper_type = paper_types[0] other_unit = paper_units[0] other_size = '' paper_orientation = paper_orientations[0] output_style = style_choices[0] output_format = format_choices[0] if self.haveTicks: tick_location = tick_locations[0] tick_placement = tick_placements border_decoration = border_decorations border_font = 'Helvetica 10' if self.haveTicks: spacing_choice = spacing_choices[0] x_major = 1.0 x_minor = 1.0 x_decimal = 3 y_major = 1.0 y_minor = 1.0 y_decimal = 3 tick_length_choice = tick_length_choices[0] tick_major = 10 tick_minor = 5 scaling_choice = scaling_choices[0] scaling = 70 x_scaling = 1.0 y_scaling = 1.0 if self.haveTicks: tick_font = 'Helvetica 10' linewidth = Output.default_linewidth if not self.haveTicks: tick_location = None tick_placement = None spacing_choice = spacing_choices[0] x_major = 1.0 x_minor = 1.0 x_decimal = 3 y_major = 1.0 y_minor = 1.0 y_decimal = 3 tick_font = 'Helvetica 10' tick_length_choice = tick_length_choices[0] tick_major = 10 tick_minor = 5 self.file_name = file_name self.title = title self.x_axis_label = x_axis_label self.y_axis_label = y_axis_label self.paper_type = paper_type self.other_unit = other_unit self.other_size = other_size self.paper_orientation = paper_orientation self.output_style = output_style self.output_format = output_format self.tick_location = tick_location self.tick_placement = tick_placement self.border_decoration = border_decoration self.border_font = border_font self.spacing_choice = spacing_choice self.x_major = x_major self.x_minor = x_minor self.x_decimal = x_decimal self.y_major = y_major self.y_minor = y_minor self.y_decimal = y_decimal self.scaling_choice = scaling_choices[0] self.scaling = scaling self.x_scaling = x_scaling self.y_scaling = y_scaling self.tick_font = tick_font self.linewidth = linewidth self.tick_length_choice = tick_length_choice self.tick_major = tick_major self.tick_minor = tick_minor def setOptionValues(self): if not hasattr(self, 'file_entry'): # it looks like on destroy can have function called but file_entry deleted already return self.file_name = file_name = self.file_entry.get() self.title = title = self.title_entry.get() self.x_axis_label = x_axis_label = self.x_axis_entry.get() self.y_axis_label = y_axis_label = self.y_axis_entry.get() n = self.size_menu.getSelectedIndex() self.paper_type = paper_type = paper_types[n] if paper_type == Output.other_paper_type: other_size = self.other_entry.get() other_unit = self.other_unit_menu.getText() else: other_size = None other_unit = None self.other_size = other_size self.other_unit = other_unit self.paper_orientation = paper_orientation = self.orientation_menu.getText() self.output_style = output_style = self.style_menu.getText() self.output_format = output_format = self.format_menu.getText() if self.haveTicks: tick_location = self.tick_menu.getText() tick_placement = self.tick_buttons.getSelected() else: tick_location = tick_placement = None self.tick_location = tick_location self.tick_placement = tick_placement self.border_decoration = border_decoration = self.border_buttons.getSelected() self.border_font = border_font = self.border_font_list.getText() if self.haveTicks: self.spacing_choice = spacing_choice = self.spacing_menu.getText() if spacing_choice != spacing_choices[0]: self.x_major = self.x_major_entry.get() self.x_minor = self.x_minor_entry.get() self.x_decimal = self.x_decimal_entry.get() self.y_major = self.y_major_entry.get() self.y_minor = self.y_minor_entry.get() self.y_decimal = self.y_decimal_entry.get() self.tick_length_choice = tick_length_choice = self.tick_length_menu.getText() if tick_length_choice != tick_length_choices[0]: self.tick_major = self.length_major_entry.get() self.tick_minor = self.length_minor_entry.get() self.scaling_choice = scaling_choice = self.scaling_menu.getText() if self.scaling_choice == scaling_choices[0]: scaling = self.scaling_scale.get() self.scaling = int(round(scaling)) else: self.x_scaling = self.x_scaling_entry.get() self.y_scaling = self.y_scaling_entry.get() if self.haveTicks: self.tick_font = self.tick_font_list.getText() self.linewidth = self.linewidth_entry.get() setOption = self.setOption if setOption: setOption('FileName', value=file_name) setOption('Title', value=title) setOption('XAxisLabel', value=x_axis_label) setOption('YAxisLabel', value=y_axis_label) if paper_type == Output.other_paper_type: setOption('OtherHeight', value=other_size[0]) setOption('OtherWidth', value=other_size[1]) setOption('OtherUnit', value=other_unit) else: paper_type = paper_type_inverse_dict[paper_type] setOption('PaperSize', value=paper_type) setOption('Orientation', value=paper_orientation) in_color = (output_style == style_choices[0]) setOption('InColor', value=in_color) output_format = format_options[format_choices.index(output_format)] setOption('OutputFormat', value=output_format) if self.haveTicks: tick_outside = (tick_location == PrintTicks.Outside) setOption('TickOutside', value=tick_outside) tick_placement = getTickPlacement2(tick_placement) setOption('TickPlacement', value=tick_placement) dateTime = (border_decorations[0] in border_decoration) fileName = (border_decorations[1] in border_decoration) setOption('ShowsDateTime', value=dateTime) setOption('ShowsFileName', value=fileName) setOption('BorderFont', value=border_font) if self.haveTicks: setOption('SpacingChoice', value=spacing_choice) if spacing_choice != spacing_choices[0]: setOption('XMajor', self.x_major) setOption('XMinor', self.x_minor) setOption('XDecimal', self.x_decimal) setOption('YMajor', self.y_major) setOption('YMinor', self.y_minor) setOption('YDecimal', self.y_decimal) setOption('TickLengthChoice', value=tick_length_choice) if tick_length_choice != tick_length_choices[0]: setOption('TickMajor', self.tick_major) setOption('TickMinor', self.tick_minor) setOption('ScalingChoice', value=scaling_choice) if scaling_choice == scaling_choices[0]: setOption('Scaling', value=0.01*self.scaling) else: setOption('XScaling', value=self.x_scaling) setOption('YScaling', value=self.y_scaling) if self.haveTicks: setOption('TickFont', self.tick_font) setOption('LineWidth', self.linewidth) def findFile(self): if self.file_select_popup: self.file_select_popup.open() else: file_types = [ FileType('All', ['*']), FileType('PostScript', ['*.ps', '*.eps']), FileType('PDF', ['*.pdf', '*.ai']) ] self.file_select_popup = FileSelectPopup(self, file_types=file_types) file = self.file_select_popup.getFile() if file: self.file_entry.set(file) def changedSize(self, entry): if entry == Output.other_paper_type: self.other_frame.grid(row=0, column=2, columnspan=2, sticky='w') else: self.other_frame.grid_forget() def changedFormat(self, entry): file_suffix = file_suffixes.get(entry) if not file_suffix: return file_name = self.file_entry.get() if not file_name: return for suffix in format_suffixes: if file_name.endswith(suffix): if suffix != file_suffix: n = len(suffix) file_name = file_name[:-n] + file_suffix self.file_entry.set(file_name) break else: file_name = file_name + file_suffix self.file_entry.set(file_name) def changedScaling(self, choice): if choice == scaling_choices[0]: self.scaling_scale.grid(row=0, column=2, columnspan=3, sticky='ew') self.x_scaling_label.grid_forget() self.x_scaling_entry.grid_forget() self.y_scaling_label.grid_forget() self.y_scaling_entry.grid_forget() else: self.scaling_scale.grid_forget() self.x_scaling_label.grid(row=0, column=2, sticky='w') self.x_scaling_entry.grid(row=0, column=3, columnspan=2, sticky='ew') self.y_scaling_label.grid(row=1, column=2, sticky='w') self.y_scaling_entry.grid(row=1, column=3, columnspan=2, sticky='ew') self.setOptionValues() def changedSpacing(self, choice): if choice == spacing_choices[0]: self.spacing_frame.grid_forget() else: self.spacing_frame.grid(row=1, column=1, columnspan=3, sticky='ew') self.setOptionValues() def changedLength(self, choice): if choice == tick_length_choices[0]: self.length_frame.grid_forget() else: self.length_frame.grid(row=1, column=0, columnspan=4, sticky='ew') self.setOptionValues() # width and height are of plot, in pixels def getOutputHandler(self, pixel_width, pixel_height, unit_width=1.0, unit_height=1.0, fonts=None): if not fonts: fonts = [] else: fonts = list(fonts) for n in range(len(fonts)): if fonts[n] == 'Times': fonts[n] = 'Times-Roman' self.setOptionValues() if not self.file_name: showError('No file', 'No file specified', parent=self) return None x_scaling = y_scaling = 1.0 if self.scaling_choice != scaling_choices[0]: try: x_scaling = float(self.x_scaling) except: showError('Bad X Scaling', 'Specified X Scaling must be floating point', parent=self) return None try: y_scaling = float(self.y_scaling) except: showError('Bad Y Scaling', 'Specified Y Scaling must be floating point', parent=self) return None if os.path.exists(self.file_name): if not showYesNo('File exists', 'File "%s" exists, overwrite?' % self.file_name, parent=self): return None if self.paper_type == Output.other_paper_type: paper_size = self.other_size + [ self.other_unit ] else: paper_size = paper_sizes[self.paper_type] output_scaling = self.scaling / 100.0 border_font = self.border_font (font, size) = border_font.split() size = int(size) border_text = {} for decoration in self.border_decoration: if decoration == 'Time and Date': location = 'se' text = time.ctime(time.time()) elif decoration == 'File Name': location = 'sw' text = self.file_name else: continue # should not be here border_text[location] = (text, font, size) if self.title: location = 'n' border_text[location] = (self.title, font, size+6) if font not in fonts: fonts.append(font) if self.haveTicks and self.tick_location == PrintTicks.Outside: axis_label_offset = 2 else: axis_label_offset = 0 outputHandler = PrintHandler.getOutputHandler(self.file_name, pixel_width, pixel_height, unit_width, unit_height, scaling_choice=self.scaling_choice, output_scaling=output_scaling, w_scaling=x_scaling, h_scaling=y_scaling, paper_size=paper_size, paper_orientation=self.paper_orientation, output_style=self.output_style, output_format=self.output_format, border_text=border_text, x_axis_label=self.x_axis_label, y_axis_label=self.y_axis_label, axis_label_font=font, axis_label_size=size, axis_label_offset=axis_label_offset, fonts=fonts, linewidth=self.linewidth, do_outline_box=self.doOutlineBox) return outputHandler def getAspectRatio(self): self.setOptionValues() if self.paper_type == Output.other_paper_type: paper_size = self.other_size else: paper_size = paper_sizes[self.paper_type] r = paper_size[1] / paper_size[0] if self.paper_orientation == 'Landscape': r = 1.0 / r return r
class CreateAxisTypePopup(BasePopup): def __init__(self, parent, *args, **kw): self.measurementType = None BasePopup.__init__(self, parent=parent, title='Create axis type', modal=True, **kw) def body(self, master): master.grid_columnconfigure(1, weight=1) row = 0 label = Label(master, text='Axis name: ', grid=(row, 0)) tipText = 'Short text name for new type of axis e.g. "17O"' self.name_entry = Entry(master, width=15, grid=(row, 1), tipText=tipText) row += 1 label = Label(master, text='Axis region: ', grid=(row, 0)) tipText = 'Comma separated values for the upper and lower bound of the axis allowed range of values' self.region_entry = FloatEntry(master, text=[0.0, 1.0], isArray=True, width=15, grid=(row, 1), tipText=tipText) row += 1 label = Label(master, text='Measurement type:', grid=(row, 0)) tipText = 'The physical entity that is being measured along the axis' self.measurement_list = PulldownList(master, tipText=tipText) self.measurement_list.grid(row=row, column=1, sticky='w') row += 1 label = Label(master, text='Dimension is sampled: ', grid=(row, 0)) tipText = 'Whether the axis is discretely sampled or a continuous range (albeit on a grid)' self.sampled_popup = BooleanPulldownMenu(master, grid=(row, 1), tipText=tipText) row += 1 label = Label(master, text='Decimal places: ', grid=(row, 0)) tipText = 'The number of decimal places that the axis values are rounded to for display purposes' self.decimals_entry = IntEntry(master, text=0, width=15, grid=(row, 1), tipText=tipText) row += 1 label = Label(master, text='Peak size: ', grid=(row, 0)) tipText = 'The relative scale for the peak symbol (i.e the "X" shape) size compared to other axes' self.peak_size_entry = FloatEntry(master, text=1.0, width=15, grid=(row, 1), tipText=tipText) row += 1 label = Label(master, text='Allowed axis units:', grid=(row, 0)) tipTexts = [ 'Units of measurement allowed for this kind of axis', ] units = [au.unit for au in self.parent.getAxisUnits()] selected = [True] * len(units) self.units_list = CheckButtons(master, units, selected=selected, direction='vertical', grid=(row, 1), tipTexts=tipTexts) row += 1 tipTexts = [ 'Make a new axis specification of the selected type and close this popup' ] texts = ['Create'] commands = [self.ok] buttons = UtilityButtonList(master, texts=texts, commands=commands, doClone=False, closeText='Cancel', helpUrl=self.help_url, grid=(row, 0), gridSpan=(1, 2), tipTexts=tipTexts) master.grid_rowconfigure(row, weight=1) self.update() def update(self, *extra): measurementType = self.measurementType measurementTypes = self.parent.getMeasurementTypes() if measurementTypes: if measurementType not in measurementTypes: self.measurementType = measurementType = measurementTypes[0] index = measurementTypes.index(measurementType) else: index = 0 self.measurementType = None self.measurement_list.setup(measurementTypes, None, index) def apply(self): name = self.name_entry.get() if (not name): showError('No name', 'Need to enter name', self) return False names = [axisType.name for axisType in self.analysisProject.axisTypes] if (name in names): showError('Repeated name', 'Name already used', self) return False region = self.region_entry.get() if ((region is None) or (len(region) != 2)): showError('Region error', 'Region must be float array of length two', self) return False if (region[0] >= region[1]): showError('Region error', 'Region must have first number < second number', self) return False measurementType = self.measurement_list.getText() isSampled = self.sampled_popup.getSelected() numDecimals = self.decimals_entry.get() if ((numDecimals is None) or (numDecimals < 0)): showError('Decimals error', 'Number of decimal places must be >= 0', self) return False peakSize = self.peak_size_entry.get() if ((peakSize is None) or (peakSize <= 0)): showError('Peak size error', 'Peak size must be > 0', self) return False selected = self.units_list.getSelected() allUnits = self.parent.getAxisUnits() axisUnits = [au for au in allUnits if au.unit in selected] self.analysisProject.newAxisType(name=name, region=region, isSampled=isSampled, axisUnits=axisUnits, numDecimals=numDecimals, peakSize=peakSize, measurementType=measurementType) return True
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 ProjectFrame(Frame): def __init__(self, guiParent, basePopup): self.basePopup = basePopup self.guiParent = guiParent self.basePopup.frameShortcuts['Project'] = self #self.registerNotify=basePopup.registerNotify #self.unregisterNotify=basePopup.unregisterNotify Frame.__init__(self, guiParent) # set up the grid self.grid_columnconfigure(0, weight=0, minsize=10) self.grid_columnconfigure(1, weight=0, minsize=50) self.grid_columnconfigure(2, weight=1, minsize=20) self.grid_columnconfigure(3, weight=0, minsize=40) self.grid_rowconfigure(0, weight=0, minsize=5) self.grid_rowconfigure(1, weight=0, minsize=10) self.grid_rowconfigure(2, weight=0, minsize=10) self.grid_rowconfigure(3, weight=0, minsize=10) # we could in theory choose to store the last visited # project in the user config file and load this on log in # if we then decide that login is mandatory then this # would give as a project from the start self.repository = None self.projectName = None self.versionTag = None # we define as much as possible once on startup and use the # grid manager to control the display thereafter # when no data self.noData_widgets = [] self.null_proj_label = Label(self, text='No Project selected') self.noData_widgets.append(self.null_proj_label) # when data self.data_widgets = [] self.proj_rep_title = Label(self, text='Project:', font='Helvetica16') self.data_widgets.append(self.proj_rep_title) self.proj_rep_label = Label(self, text='Repository:') self.data_widgets.append(self.proj_rep_label) self.proj_rep_value = Label(self, text='') self.data_widgets.append(self.proj_rep_value) # This is more complex than it seems. We need to have some # user information accoisated with this too (who has the lock) self.proj_status_label = Label(self, text = 'Status:') self.data_widgets.append(self.proj_status_label) self.proj_status_value = Label(self, text='') self.data_widgets.append(self.proj_status_value) self.proj_details_label = Label(self, text='Description:') self.proj_details = Text(self, width=75, height=6, text='') self.data_widgets.append(self.proj_details) info_modes = ['versions','tasks','raw data'] self.info_mode_select = PulldownList(self, self.tmpCall, info_modes) self.data_widgets.append(self.info_mode_select) initial_cols = ['Project','Version','Created by Task', 'Created from Project version'] self.proj_history_matrix = ScrolledMatrix(self, headingList=initial_cols, initialRows=7, doubleCallback=self.history_callback) self.data_widgets.append(self.proj_history_matrix) initial_cols = ['ID','Task','User','Status', 'Date'] self.proj_tasks_matrix = ScrolledMatrix(self, headingList=initial_cols, initialRows=7, doubleCallback=self.history_callback) self.data_widgets.append(self.proj_tasks_matrix) initial_cols = ['name','type','size','details'] self.proj_files_matrix = ScrolledMatrix(self, headingList=initial_cols, initialRows=7, doubleCallback=self.history_callback) self.data_widgets.append(self.proj_files_matrix) self.proj_export_button = Button(self, height=1, width=14,text='Export', command=self.export_project) self.data_widgets.append(self.proj_export_button) self.proj_import_button = Button(self, height=1, width=14,text='Import', command=self.export_project) self.data_widgets.append(self.proj_import_button) self.run_testtask1_button = Button(self, height=1, width=14,text='Run TestTask1', command=self.goto_task1_tab) self.data_widgets.append(self.run_testtask1_button) # FIXME JMCI # insert this inside this Frame directly: not worth abstracting out self.filter_frame = FilterFrame(self, self.basePopup, text='Filter') self.data_widgets.append(self.filter_frame) self.drawFrame() # Refreshing of the Frame is meaningless without setting the full # connection parameters def drawFrame(self): if self.basePopup.repList: self.repository = self.basePopup.repList.currentRepository if self.repository: self.projectName = self.repository.currentProjectName self.versionTag = self.repository.currentVersionTag print 'in drawFrame ', self.repository, self.projectName, self.versionTag if self.projectName == None: for widget in self.data_widgets: widget.grid_remove() self.null_proj_label.grid(row=1, column=1, columnspan=4, sticky='nswe') else: # build up the body. for widget in self.noData_widgets: widget.grid_remove() # OK, so we need to determine the project that we are showing # in this frame. To uniquely describe a project we need to # know the repository, the project name, and the project version # so all these need to be set somehow. Maybe the best approach is # so have a public method that takes these arguments and to force # every refresh to operate through this. Then the drawFrame() method # would be private. loc = SharedBeanServiceLocator() port = loc.getSharedBean() # need two calls, one for the Project and one for the version hm1 = {'name': self.projectName } wsstr_in1 = WSString(hm1) request1 = getFields(); request1._arg0 = 'org.pimslims.applet.server.ProjectBean'; request1._arg1 = 'getFields'; request1._arg2 = wsstr_in1.str response1 = port.getFields(request1) wsstr_out1 = WSString(response1._return) hm_proj = wsstr_out1.getStructHM() # now the version hm2 = {'project' : { 'name': self.projectName }, 'versionTag': self.versionTag } wsstr_in2 = WSString(hm2) request2 = getFields(); request2._arg0 = 'org.pimslims.applet.server.ProjectVersionBean'; request2._arg1 = 'getFields'; request2._arg2 = wsstr_in2.str response2 = port.getFields(request2) wsstr_out2 = WSString(response2._return) hm_pv = wsstr_out2.getStructHM() self.proj_rep_title.set('Project: ' + self.projectName + ' v ' + self.versionTag) self.proj_rep_title.grid(row=1, column=1, sticky='w') self.proj_rep_label.grid(row=2, column=1, sticky='w') self.proj_rep_value.set(self.repository.name) self.proj_rep_value.grid(row=2, column=2, sticky='w') status = 'N/A' if hm_pv['status'] != 'valIsNullPointer': status = hm_pv['status'] self.proj_status_label.grid(row=3, column=1, sticky='w') self.proj_status_value.set(status) self.proj_status_value.grid(row=3, column=2, sticky='w') # also want to know: # * how this file was created # * what external file (e.g. spectrum files) it uses # * the log history # * what processes it has been used in # * if locked, what process, user is responsible for this details = 'N/A' if hm_proj['details'] != 'valIsNullPointer': details = hm_proj['details'] self.proj_details_label.grid(row=4, column=1, columnspan=2, sticky='nw') self.proj_details.setText(details) self.proj_details.grid(row=4, column=2, pady=10, sticky='nw') self.info_mode_select.grid(row=5, column=1, sticky='w') # this needs to go into a separate subroutine # get al the version hm3 = {'project' : { 'name': self.projectName }} wsstr_in3 = WSString(hm3) request3 = getList() request3._arg0 = 'org.pimslims.applet.server.ProjectVersionBean'; request3._arg1 = 'getListWithFields'; request3._arg2 = wsstr_in3.str response3 = port.getList(request3) wsstr_out3 = WSString(response3._return) vers = wsstr_out3.getStruct() matrix = []; objs = []; print 'PROJECT: ', vers for ver in vers: taskSerial = 'unknown' if (ver.has_key('createdByTask')): task = ver['createdByTask'] taskSerial = task['serial'] # FIXME JMCI # Really should get this from the task, of it exists parentVersionTag = 'unknown' if (ver.has_key('sourceVersion')): pv = ver['sourceVersion'] pvp = pv['project'] parentName = pvp['name'] parentVersionTag = pvp['name'] + ' ' + pv['versionTag'] new_row = (self.projectName, ver['versionTag'], taskSerial, parentVersionTag ) matrix.append(new_row) objs.append(ver) self.proj_history_matrix.update(objectList=objs, textMatrix=matrix) self.proj_history_matrix.grid(row=6, column=1, columnspan=2, pady=10, sticky='nsew') self.filter_frame.grid(row=6, column=3, pady=10, sticky='n') # have a button to export. It should now be clear which version # of which project in which repository should be exported self.proj_import_button.grid(row=1, column=3, padx=3,pady=3, sticky='ew') self.proj_export_button.grid(row=2, column=3, padx=3,pady=3, sticky='ew') self.run_testtask1_button.grid(row=4, column=3, padx=3,pady=3, sticky='ew') # It is not clear precisely how methods should be triggered from # the project frame, as there may be >1 input to a given task with # a specific role. It maybe that we need to parse out a metadescription # of the task and use this to produce the correct buttons (maybe # even depending on the contents of the file ?) #self.task_button_texts=['Test1Task','Test2Task'] #self.task_button_cmds=[self.runTest1Task, self.runTest2Task] #self.task_button_list = ButtonList(self, self.task_button_texts, self.task_button_cmds, # None, 1, 1, True, None, Tkinter.VERTICAL) # #self.task_button_list.grid(row=1, rowspan=3, column=4, padx=3, sticky='w') def history_callback (self, obj, row, col): if (col == 1): pv = obj['versionTag'] print 'PROJECT -> PROJECT (DIRECT) ', pv self.repository.currentVersionTag = pv.__str__() self.drawFrame() elif (col == 2): task = obj['createdByTask'] taskSerial = task['serial'] print 'PROJECT -> TASK ', taskSerial self.basePopup.currentTask = taskSerial.__str__() # FIXME # This is more complex. Need to navigate to the correct # panel within the Task submenu if self.basePopup.frameShortcuts.has_key('Task'): taskFrame = self.basePopup.frameShortcuts['Task'] # really need to check on the type of task if taskFrame.frameShortcuts.has_key('Test1'): taskFrame.frameShortcuts['Test1'].drawFrame() # also need to select the current tabbed frame self.basePopup.tabbedFrame.select(2) # FIXME JMCI # need to look into this. Really we need both the project and # the version for this (in theory this could be based on a different # project although it is highly unlikely!) elif (col == 3): pv = obj['sourceVersion'] parentVersion = pv['versionTag'] pvp = pv['project'] parentName = pvp['name'] print 'PROJECT -> PROJECT (SOURCE) ', parentVersion self.repository.currentVersionTag = parentVersion.__str__() self.repository.currentProjectName = parentName.__str__() self.drawFrame() # create a new task with this project as an input # it is not clear how we should do this:- construct data locally # and then save, or save a first cut and then ammend. On the basis # that the serial has to be generated by the DB, we will go for the # former for now # it would be better to pass this to a processor layer that # determines parameters and then builds the stub. def goto_task1_tab(self): # create a new task (provisional status) loc = SharedBeanServiceLocator() port = loc.getSharedBean() #hm0 = { 'project': { 'name': self.projectName}, # 'versionTag' : self.versionTag, # '_handle' : 'findOrFail' } # FIXME # We have an error here (it seems to be a problem between the CCPN # and hibernate layers that is not picking objects as the same for # some reason. Therefore do not set the inputVersion for now hm0 = { 'project': { 'name': self.projectName}, 'versionTag' : self.versionTag} hm2 = { 'inputVersion' : hm0, 'status' : 'PROVISIONAL' } #hm2 = { 'status' : 'PROVISIONAL' } wsstr_in1 = WSString(hm2) req1 = recordAndLoad(); req1._arg0 = 'org.pimslims.applet.server.TaskBean'; req1._arg1 = 'recordTest1'; req1._arg2 = wsstr_in1.str resp1 = port.recordAndLoad(req1) wsstr_out1 = WSString(resp1._return) v = wsstr_out1.getStructHM() self.basePopup.currentTask = v['serial'].__str__() # FIXME # This is more complex. Need to navigate to the correct # panel within the Task submenu if self.basePopup.frameShortcuts.has_key('Task'): taskFrame = self.basePopup.frameShortcuts['Task'] # really need to check on the type of task if taskFrame.frameShortcuts.has_key('Test1'): taskFrame.frameShortcuts['Test1'].drawFrame() # also need to select the current tabbed frame self.basePopup.tabbedFrame.select(2) # method for exporting repository directory to local file # system. This is a very basic approach, essentially first # listing all the files required for transfer and then # copying them over one by one. Note that due to limitations # in ZSI we cannot do much better than base64 encoding each # file. We could consider storing a tgz on the server and # exporting that. It might be a considerable improvement as # the compression leads to a file < 10% of the original size # which would cut down on traffic enormously def export_project(self): versionTag = self.versionTag name = self.projectName # need to preserve the current status. Take a default from the # Wms layer rootDir = self.basePopup.repList.current_export_dir # This should be obtained from a query box. Hard code for now fileSelectPopup = FileSelectPopup(self, None, rootDir) exp_project_dir = fileSelectPopup.getDirectory() self.basePopup.repList.current_export_dir = exp_project_dir # These should come from the repository object loc = SharedBeanServiceLocator() port = loc.getSharedBean() req1 = getList() hm1 = {'projectName': name, 'versionTag': versionTag, 'segment': 'jionides' } wsstr1 = WSString(hm1) # these are actually static req1._arg0 = 'org.pimslims.applet.server.CcpnFileBean'; req1._arg1 = 'getList'; req1._arg2 = wsstr1.str # get the response resp1 = port.getList(req1) # this is a hack at present. It needs to be written properly wsstr = WSString(resp1._return) ss = wsstr.getStruct() print 'got array ', ss for strg in ss: print 'trying to handle file ', strg hm2 = {'projectName': name, 'versionTag': versionTag, 'segment': 'jionides', 'fileName' : '/' + strg.__str__(), 'random' : 'test', 'raw' : False} print 'trying to handle file 2 ', strg print 'trying to handle file 2 ', hm2 req2 = getDocWithParams() wsstr2 = WSString(hm2) # these are actually static req2._arg0 = 'org.pimslims.applet.server.CcpnFileBean'; req2._arg1 = wsstr2.str # get the response resp2 = port.getDocWithParams(req2) local_file_path = exp_project_dir + '/' + name + '/' + strg idx = local_file_path.rfind('/') local_dir = local_file_path[:idx] print 'trying to create ', local_dir os.makedirs(local_dir) print 'writing to local area; ', local_file_path f = open(local_file_path, 'w') f.write(resp2._return) # it is likely that these methods are going to have to be highly specialised # as some tasks are going to have many input files and we need to know which # role the project we are in takes in the task. def runTest1Task(self): pass def runTest2Task(self): pass def view_project(self, project): self.project = project self.drawFrame() def tmpCall(self): return def administerNotifiers(self, notifyFunc): for func in ('__init__','delete','setName'): notifyFunc(self.updateAllAfter, 'ccp.nmr.Nmr.Experiment', func) notifyFunc(self.updateAllAfter, 'ccp.nmr.Nmr.DataSource', func) def updateAllAfter(self, obj): self.after_idle(self.updateAll) def updateAll(self, project=None): return def quit(self): self.guiParent.parent.destroy() def destroy(self): self.administerNotifiers(self.basePopup.unregisterNotify) Frame.destroy(self)
class EditPeakFindParamsPopup(BasePopup): """ ** Peak Settings and Non-Interactive Peak Finding ** The purpose of this dialog is to allow the user to select settings for finding and integrating peaks, and also to be able to find peaks in an arbitrary region that is specified in a table rather than via a spectrum window. ** Find Parameters tab ** This can be used to specify how peak finding works. First of all, you can search for just positive peaks, just negative peaks or both, and the default is that it is just positive peaks. However, this is further filtered by what the contour levels are. If there are no positive contour levels for a given spectrum then positive peaks are not found even if this dialog says they can be, and similarly if there are no negative contour levels for a given spectrum then negative peaks are not found even if this dialog says they can be. The peak finding algorithm looks for local extrema (maximum for positive peaks and minima for negative peaks). But on a grid there are various ways to define what you mean by an extremum. Suppose you are trying to determine if point p is a maximum (similar considerations apply for minimum). You would want the intensity at all nearby points to be less than or equal to the intensity at p. You can just check points that are just +- one point from p in each dimension, or you can also check "diagonal" points. For example, if you are looking at point p = (x, y) in 2D, then the former would mean checking the four points (x-1, y), (x+1, y) (x, y-1) and (x, y+1), whereas for the latter you would also have to check (x-1, y-1), (x-1, y+1), (x+1, y-1) and (x+1, y+1). In N dimensions the "diagonal" method involves checking 3^N-1 points whereas the "non-diagonal" method involves checking only 2N points. In general the "non-diagonal" method is probably the one to use, and it is the default. Peaks are only found above (for positive peaks) or below (for negative peaks) some threshold. By default this is determined by the contour level for the spectrum. For positive peaks the threshold is the minimum positive contour level, and for negative peaks the threshold is the maximum negative contour level. However these levels can be scaled up (or down) using the "Scale relative to contour levels" option (default value 1). For example, if you have drawn the contour levels low to show a bit of noise, but do not want the noise picked as peaks, then you could select a scale of 2 (or whatever) to increase the threshold. The "Exclusion buffer around peaks" is so that in crowded regions you do not get too many peaks near one location. By default the exclusion buffer is 1 point in each dimension, but this can be increased to make the algorithm find fewer peaks. By default the peak finding only looks at the orthogonal region that is displayed in the given window where peak finding is taking place. Sometimes it looks like a peak should be found because in x, y you can see an extremum, but unless it is also an extremum in the orthogonal dimensions it is not picked. You can widen out the points being examined in the orthogonal dimensions by using the "Extra thickness in orthogonal dims" option, which is specified in points. The "Minimum drop factor" is by what factor the intensity needs to drop from its extreme value for there to be considered to be a peak. This could help remove sinc wiggle peaks, for example. The default is that the drop factor is 0, which in effect means that there is no condition. The "Volume method" is what is used to estimate the volume of peaks that are found. The default is "box sum", which just looks at a fixed size box around the peak centre and sums the intensities in that. The size of the box is set in the table in the Spectrum Widths tab. The "truncated box sum" is the same as "box sum" except that the summing stops in a given direction when (if) the intensities start increasing. The "parabolic" fit fits a quadratic equation in each dimension to the intensity at the peak centre and ad +- 1 points and then uses the equivalent Gaussian fit to estimate the volume. ** Spectrum Widths ** This can be used to specify minimum linewidths (in Hz) for there to be considered a peak to exist in the peak finding algorithm. It is also where the Boxwidth for each dimension in each spectrum is specified. ** Diagonal Exclusions ** This can be used to exclude peaks from being found in regions near the diagonal (so in homonuclear experiments). The exclusion region is specified in ppm and is independent of spectrum. ** Region Peak Find ** This can be used to find peaks non-interactively (so not having to control shift drag inside a spectrum window). The region being analysed is specified in the table. There are two types of conditions that can be specified, "include" for regions that should be included and "exclude" for regions that should be excluded. The regions are specified in ppm. The "Whole Region" button will set the selected row in the table to be the entire fundamental region of the spectrum. The "Add Region" button adds an extra row to the table, and the "Delete Region" button removes the selected row. The "Adjust Params" button goes to the Find Parameters tab. The "Find Peaks!" button does the peak finding. """ def __init__(self, parent, *args, **kw): self.spectrum = None BasePopup.__init__(self, parent=parent, title='Peak : Peak Finding', **kw) def body(self, guiFrame): self.geometry('600x350') guiFrame.expandGrid(0, 0) tipTexts = ['', '', '', ''] options = [ 'Find Parameters', 'Spectrum Widths', 'Diagonal Exclusions', 'Region Peak Find' ] tabbedFrame = TabbedFrame(guiFrame, options=options, grid=(0, 0)) frameA, frameB, frameC, frameD = tabbedFrame.frames self.tabbedFrame = tabbedFrame # Find Params frameA.expandGrid(2, 0) row = 0 label = LabelFrame(frameA, text='Extrema to search for:', grid=(row, 0), gridSpan=(1, 2)) label.expandGrid(0, 1) entries = ['positive and negative', 'positive only', 'negative only'] tipTexts = [ 'Sets whether peak picking within spectra find intensity maxima, minima or both maxima and minima', ] self.extrema_buttons = RadioButtons(label, entries=entries, select_callback=self.apply, direction='horizontal', grid=(0, 0), tipTexts=tipTexts) row += 1 label = LabelFrame(frameA, text='Nearby points to check:', grid=(row, 0), gridSpan=(1, 2)) label.expandGrid(None, 1) entries = ['+-1 in at most one dim', '+-1 allowed in any dim'] tipTexts = [ 'Sets how permissive the peak picking in when searching for intensity extrema; by adding extra points to the selected search region', ] self.adjacent_buttons = RadioButtons(label, entries=entries, select_callback=self.apply, direction='horizontal', grid=(0, 0), tipTexts=tipTexts) row += 1 labelFrame = LabelFrame(frameA, text='Other parameters:', grid=(row, 0), gridSpan=(1, 2)) labelFrame.expandGrid(5, 2) frow = 0 label = Label(labelFrame, text='Scale relative to contour levels:', grid=(frow, 0), sticky='e') tipText = 'Threshold above which peaks are picked, relative to the lowest displayed contour; 1.0 means picking exactly what is visible' self.scale_entry = FloatEntry(labelFrame, grid=(frow, 1), tipText=tipText, returnCallback=self.apply, width=10) self.scale_entry.bind('<Leave>', self.apply, '+') frow += 1 label = Label(labelFrame, text='Exclusion buffer around peaks (in points):', grid=(frow, 0), sticky='e') tipText = 'The size of the no-pick region, in data points, around existing picked peaks; eliminates duplicate picking' self.buffer_entry = IntEntry(labelFrame, returnCallback=self.apply, grid=(frow, 1), width=10, tipText=tipText) self.buffer_entry.bind('<Leave>', self.apply, '+') frow += 1 label = Label(labelFrame, text='Extra thickness in orthogonal dims (in points):', grid=(frow, 0), sticky='e') tipText = 'Sets whether to consider any additional planes (Z dimension) when calculating peak volume integrals' self.thickness_entry = IntEntry(labelFrame, returnCallback=self.apply, width=10, grid=(frow, 1), tipText=tipText) self.thickness_entry.bind('<Leave>', self.apply, '+') frow += 1 label = Label(labelFrame, text='Minimum drop factor (0.0-1.0):', grid=(frow, 0), sticky='e') tipText = '' self.drop_entry = FloatEntry(labelFrame, returnCallback=self.apply, width=10, grid=(frow, 1), tipText=tipText) self.drop_entry.bind('<Leave>', self.apply, '+') frow += 1 label = Label(labelFrame, text='Volume method:', grid=(frow, 0), sticky='e') tipText = 'Selects which method to use to calculate peak volume integrals when peaks are picked; box sizes are specified in "Spectrum Widths"' self.method_menu = PulldownList(labelFrame, texts=PeakBasic.PEAK_VOLUME_METHODS, grid=(frow, 1), callback=self.apply, tipText=tipText) # Spectrum widths frameB.expandGrid(1, 1) label = Label(frameB, text='Spectrum: ') label.grid(row=0, column=0, sticky='e') tipText = 'The spectrum which determines the widths being shown' self.expt_spectrum = PulldownList(frameB, tipText=tipText, callback=self.setSpectrumProperties) self.expt_spectrum.grid(row=0, column=1, sticky='w') self.editLinewidthEntry = FloatEntry(self, text='', returnCallback=self.setLinewidth, width=10) self.editBoxwidthEntry = FloatEntry(self, text='', returnCallback=self.setBoxwidth, width=10) tipTexts = [ 'The number of the spectrum dimension to which the settings apply', 'The nuclear isotope measures in the spectrum dimension', 'The smallest value for the linewidth of a peak for it to be picked', 'The size of the spectrum region to perform the volume integral over' ] headingList = [ 'Dimension', 'Isotope', 'Minimum Linewidth (Hz)', 'Boxwidth' ] editSetCallbacks = [None, None, self.setLinewidth, self.setBoxwidth] editGetCallbacks = [None, None, self.getLinewidth, self.getBoxwidth] editWidgets = [ None, None, self.editLinewidthEntry, self.editBoxwidthEntry ] self.spectrumMatrix = ScrolledMatrix(frameB, initialRows=6, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, headingList=headingList, callback=self.selectCell, tipTexts=tipTexts) self.spectrumMatrix.grid(row=1, column=0, columnspan=2, sticky='nsew') # Diagonal Exclusions frameC.expandGrid(0, 0) tipTexts = [ 'The isotope as measures on the axis of a spectrum window', 'The distance from the homonuclear diagonal line within which no peak picking can occur' ] self.exclusionEntry = FloatEntry(self, text='', returnCallback=self.setExclusion, width=10) headingList = ['Isotope', 'Diagonal Exclusion (ppm)'] editSetCallbacks = [None, self.setExclusion] editGetCallbacks = [None, self.getExclusion] editWidgets = [None, self.exclusionEntry] self.isotopeMatrix = ScrolledMatrix(frameC, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, headingList=headingList, grid=(0, 0), tipTexts=tipTexts) # Region peak find self.regionFindPeakList = None self.regionCondition = None self.regionConditions = [] self.regionCol = 1 row = 0 label = Label(frameD, text='Peak List: ', grid=(0, 0)) tipText = 'Selects which peak list to perform region-wide peak picking for' self.regionPeakListPulldown = PulldownList( frameD, callback=self.changeRegionPeakList, grid=(0, 1), tipText=tipText) row += 1 frameD.expandGrid(row, 1) self.regionEntry = FloatEntry(self, text='', returnCallback=self.setRegion, width=10) self.conditionMenu = PulldownList(self, texts=('include', 'exclude'), callback=self.setCondition) tipTexts = [ 'Whether to include or exclude the states region from region-wide peak picking', ] headingList = ['Condition'] editSetCallbacks = [None] editGetCallbacks = [None] editWidgets = [self.conditionMenu] self.regionFindMatrix = ScrolledMatrix( frameD, headingList=headingList, callback=self.selectRegionCell, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, grid=(row, 0), gridSpan=(1, 2)) row += 1 tipTexts = [ 'Sets the currently selected region row to cover the whole spectrum', 'Add a new region row, which may them be set for exclusion or inclusion when peak picking large areas', 'Remove the selected region specification', 'Go to the panel for setting the parameters that control how peaks extrema are picked', 'Using the stated regions and parameters, perform region-wide peak picking' ] texts = [ 'Whole Region', 'Add Region', 'Delete Region', 'Adjust Params', 'Find Peaks!' ] commands = [ self.wholeRegion, self.addCondition, self.deleteCondition, self.adjustParams, self.regionFindPeaks ] buttons = ButtonList(frameD, texts=texts, commands=commands, grid=(row, 0), gridSpan=(1, 2), tipTexts=tipTexts) buttons.buttons[4].config(bg='#B0FFB0') utilButtons = UtilityButtonList(tabbedFrame.sideFrame, grid=(0, 0), helpUrl=self.help_url, sticky='e') self.dataDim = None self.setParamsEntries() self.updateSpectrum() self.setIsotopeProperties() self.updateRegionPeakLists() self.administerNotifiers(self.registerNotify) def administerNotifiers(self, notifyFunc): # Many more needed here, esp on the AnalysisProject prams for func in ('__init__', 'delete', 'setName'): notifyFunc(self.updateRegionPeakLists, 'ccp.nmr.Nmr.DataSource', func) notifyFunc(self.updateRegionPeakLists, 'ccp.nmr.Nmr.Experiment', func) for func in ('__init__', 'delete'): notifyFunc(self.updateRegionPeakLists, 'ccp.nmr.Nmr.PeakList', func) for clazz in ('Experiment', 'DataSource'): for func in ('__init__', 'delete', 'setName'): notifyFunc(self.updateSpectrumTable, 'ccp.nmr.Nmr.%s' % clazz, func) for func in ('setPeakFindBoxWidth', 'setPeakFindMinLineWidth'): notifyFunc(self.updateSpectrumTable, 'ccpnmr.Analysis.AnalysisDataDim', func) def destroy(self): self.administerNotifiers(self.unregisterNotify) BasePopup.destroy(self) def updateSpectrum(self, spectrum=None): if not spectrum: spectrum = self.spectrum spectra = self.parent.getSpectra() if spectra: if spectrum not in spectra: spectrum = spectra[0] index = spectra.index(spectrum) names = ['%s:%s' % (x.experiment.name, x.name) for x in spectra] else: index = 0 names = [] self.expt_spectrum.setup(names, spectra, index) self.setSpectrumProperties(spectrum) def updateNotifier(self, *extra): self.updateSpectrum() def setLinewidth(self, *event): value = self.editLinewidthEntry.get() if value is not None: PeakFindParams.setPeakFindMinLinewidth(self.dataDim, value) self.setSpectrumProperties(self.dataDim.dataSource) def getLinewidth(self, dataDim): if dataDim: self.editLinewidthEntry.set( PeakFindParams.getPeakFindMinLinewidth(self.dataDim)) def setBoxwidth(self, *event): value = self.editBoxwidthEntry.get() if value is not None: PeakFindParams.setPeakFindBoxwidth(self.dataDim, value) self.setSpectrumProperties(self.dataDim.dataSource) def getBoxwidth(self, dataDim): if dataDim: self.editBoxwidthEntry.set( PeakFindParams.getPeakFindBoxwidth(self.dataDim)) def selectCell(self, object, row, col): self.dataDim = object def setExclusion(self, *extra): isotope = self.isotopeMatrix.currentObject if not isotope: return value = self.exclusionEntry.get() if value is not None: setIsotopeExclusion(isotope, value) self.setIsotopeProperties() def getExclusion(self, isotope): value = getIsotopeExclusion(isotope) self.exclusionEntry.set(value) def setParamsEntries(self): project = self.project params = PeakFindParams.getPeakFindParams(project) self.scale_entry.set(params['scale']) self.buffer_entry.set(params['buffer']) self.thickness_entry.set(params['thickness']) self.drop_entry.set(params['drop']) volumeMethod = params['volumeMethod'] if volumeMethod == 'parabolic fit': volumeMethod = PeakBasic.PEAK_VOLUME_METHODS[0] self.method_menu.set(params['volumeMethod']) if (params['nonadjacent']): n = 1 else: n = 0 self.adjacent_buttons.setIndex(n) have_high = params['haveHigh'] have_low = params['haveLow'] if (have_high and have_low): n = 0 elif (have_high): n = 1 else: n = 2 self.extrema_buttons.setIndex(n) def apply(self, *extra): params = {} params['scale'] = self.scale_entry.get() params['buffer'] = self.buffer_entry.get() params['thickness'] = self.thickness_entry.get() params['drop'] = self.drop_entry.get() params['volumeMethod'] = self.method_menu.getText() n = self.adjacent_buttons.getIndex() if (n == 0): nonadjacent = False else: nonadjacent = True params['nonadjacent'] = nonadjacent n = self.extrema_buttons.getIndex() if (n == 0): have_high = True have_low = True elif (n == 1): have_high = True have_low = False elif (n == 2): have_high = False have_low = True params['haveHigh'] = have_high params['haveLow'] = have_low project = self.project try: PeakFindParams.setPeakFindParams(project, params) except Implementation.ApiError, e: showError('Parameter error', e.error_msg, parent=self)
class DangleFrame(Frame): def __init__(self, parent, dangleGui, project=None, *args, **kw): self.guiParent = parent self.dangleGui = dangleGui self.dangleDir = None self.dangleChain = None self.dangleResidue = None #self.outDir = OUTDIR self.row = None self.col = None self.project = project self.nmrProject = None self.colorScheme = 'red' self.chain = None self.shiftList = None self.dangleStore = False # Not None self.constraintSet = None self.ensemble = None Frame.__init__(self, parent=parent) self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(1, weight=1) row = 0 # TOP LEFT FRAME frame = LabelFrame(self, text='Options') frame.grid(row=row, column=0, sticky='nsew') frame.columnconfigure(5, weight=1) label = Label(frame, text='Chain') label.grid(row=0, column=0, sticky='w') self.chainPulldown = PulldownList( frame, callback=self.changeChain, tipText='Choose the molecular system chain to make predictions for' ) self.chainPulldown.grid(row=0, column=1, sticky='w') label = Label(frame, text='Shift List') label.grid(row=0, column=2, sticky='w') self.shiftListPulldown = PulldownList( frame, callback=self.changeShiftList, tipText='Select the shift list to take input chemical shifts from') self.shiftListPulldown.grid(row=0, column=3, sticky='w') label = Label(frame, text='Max No. of Islands:') label.grid(row=0, column=4, sticky='w') sizes = range(10) texts = [str(s) for s in sizes] + [ 'Do not reject', ] self.rejectPulldown = PulldownList( frame, texts=texts, objects=sizes + [ None, ], tipText= 'Select the maximum allowed number of disontinuous prediction islands' ) self.rejectPulldown.set(DEFAULT_MAX_ISLANDS) # Actual value not index self.rejectPulldown.grid(row=0, column=5, sticky='w') label = Label(frame, text='Dangle Run:') label.grid(row=1, column=0, sticky='w') self.dangleStorePulldown = PulldownList( frame, callback=self.changeDangleStore, tipText='Select a run number to store DANGLE results within') self.dangleStorePulldown.grid(row=1, column=1, sticky='w') label = Label(frame, text='Ensemble:') label.grid(row=1, column=2, sticky='w') self.ensemblePulldown = PulldownList( frame, callback=self.changeEnsemble, tipText= 'Select the structure ensemble for superimposition of angle values on the GLE plots' ) self.ensemblePulldown.grid(row=1, column=3, sticky='w') label = Label(frame, text='Restraint Set:') label.grid(row=1, column=4, sticky='w') self.constrSetPulldown = PulldownList( frame, callback=self.changeConstraintSet, tipText= 'Select the CCPN restraint set to store DANGLE dihedral angle restraints in' ) self.constrSetPulldown.grid(row=1, column=5, sticky='w') # TOP RIGHT FRAME outerFrame = Frame(self) outerFrame.grid(row=row, column=1, rowspan=2, sticky='nsew') outerFrame.rowconfigure(0, weight=1) outerFrame.columnconfigure(0, weight=1) frame = LabelFrame(outerFrame, text='Global Likelihood Estimates') frame.grid(row=0, column=0, sticky='nsew') frame.rowconfigure(1, weight=1) frame.columnconfigure(2, weight=1) self.prevPlot = ViewRamachandranFrame(frame, relief='sunken', defaultPlot=False, width=180, height=180, bgColor=self.cget('bg'), nullColor='#000000', titleText='Previous', xTicks=False, yTicks=False, xLabel='', yLabel='', showCoords=False) self.prevPlot.grid(row=0, column=0, sticky='nsew') self.prevPlot.getPlotColor = self.getPlotColor self.nextPlot = ViewRamachandranFrame(frame, relief='sunken', defaultPlot=False, width=180, height=180, bgColor=self.cget('bg'), nullColor='#000000', titleText='Next', xTicks=False, yTicks=False, xLabel='', yLabel='', showCoords=False) self.nextPlot.grid(row=0, column=1, sticky='nsew') self.nextPlot.getPlotColor = self.getPlotColor self.plot = ViewRamachandranFrame(frame, relief='sunken', defaultPlot=False, width=360, height=360, bgColor=self.cget('bg'), nullColor='#000000') self.plot.grid(row=1, column=0, columnspan=2, sticky='nsew') self.plot.selectColor = '#FFB0B0' self.plot.getPlotColor = self.getPlotColor # BOTTOM RIGHT FRAME frame = Frame(outerFrame) frame.grid(row=1, column=0, sticky='nsew') frame.rowconfigure(0, weight=1) frame.columnconfigure(0, weight=1) texts = ('Previous', ' Next ') commands = (self.showPrevious, self.showNext) tipTexts = [ 'Show GLE plot of angle predictions for previous residue in chain', 'Show GLE plot of angle predictions for next residue in chain' ] buttonList = ButtonList(frame, texts, commands, tipTexts=tipTexts) buttonList.grid(row=0, column=0, sticky='nsew') row += 1 # BOTTOM LEFT FRAME frame = LabelFrame(self, text='Dihedral Angle Predictions') frame.grid(row=row, column=0, sticky='nsew') frame.grid_columnconfigure(0, weight=1) frame.grid_rowconfigure(0, weight=1) self.floatEntry = FloatEntry(self, text='', returnCallback=self.setFloatEntry, width=10, formatPlaces=9) tipTexts = [ 'Residue number in chain', 'Residue type code', 'Number of high scoring discontinuous angle predictions', 'Predicted secondary structure code', 'Predicted phi dihedral angle (CO-N-CA-CO)', 'Predicted psi dihedral angle (N-CA-CO-N)', 'Upper bound of phi angle prediction', 'Lower bound of phi angle prediction', 'Upper bound of psi angle prediction', 'Lower bound of phi angle prediction', 'Chemical shifts used in prediction' ] headingList = [ 'Res\nNum', 'Res\nType', 'No. of\nIslands', 'SS', 'Phi', 'Psi', 'Phi\nUpper', 'Phi\nLower', 'Psi\nUpper', 'Psi\nLower', 'Chemical Shifts' ] editWidgets = [ None, None, None, None, self.floatEntry, self.floatEntry, self.floatEntry, self.floatEntry, self.floatEntry, self.floatEntry ] editGetCallbacks = [ None, None, None, None, self.getFloatEntry, self.getFloatEntry, self.getFloatEntry, self.getFloatEntry, self.getFloatEntry, self.getFloatEntry ] editSetCallbacks = [ None, None, None, None, self.setFloatEntry, self.setFloatEntry, self.setFloatEntry, self.setFloatEntry, self.setFloatEntry, self.setFloatEntry ] self.predictionMatrix = ScrolledMatrix( frame, headingList=headingList, multiSelect=True, callback=self.selectCell, tipTexts=tipTexts, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks) # doubleCallback=self.loadGLEs) self.predictionMatrix.grid(row=0, column=0, sticky='nsew') row += 1 tipTexts = [ 'Remove the predictions for the selected residues', 'Run the DANGLE method to predict dihedral angles and secondary structure', 'Delete the DANGLE results stored under the current run number', 'Store the angle predictions and bounds in a new CCPN dihedral angle restraint list', 'Store the secondary structure predictions in the CCPN project' ] texts = [ 'Clear\nSelected', 'Run Prediction!', 'Delete\nCurrent Run', 'Commit\nRestraints', 'Commit\nSecondary Structure' ] commands = [ self.clearSelected, self.runDangle, self.deleteRun, self.storeDihedralConstraints, self.storeSecondaryStructure ] self.buttonList = createDismissHelpButtonList( self, texts=texts, commands=commands, # dismiss_text='Quit', dismiss_cmd=self.dangleGui.quit, help_url=self.dangleGui.help_url, expands=True, tipTexts=tipTexts) self.buttonList.grid(row=row, column=0, columnspan=2, sticky='ew') self.buttonList.buttons[1].config(bg='#C0FFFF') self.updateProject(project) self.notify(dangleGui.registerNotify) def destroy(self): self.notify(self.dangleGui.unregisterNotify) Frame.destroy(self) def notify(self, notifyfunc): for func in ('__init__', 'delete'): notifyfunc(self.updateChainPulldown, 'ccp.molecule.MolSystem.Chain', func) for func in ('__init__', 'delete', 'setName'): notifyfunc(self.updateShiftListPulldown, 'ccp.nmr.Nmr.ShiftList', func) for func in ('__init__', 'delete'): notifyfunc(self.updateConstrSetPulldown, 'ccp.nmr.NmrConstraint.NmrConstraintStore', func) for func in ('__init__', 'delete'): notifyfunc(self.updateEnsemblePulldown, 'ccp.molecule.MolStructure.StructureEnsemble', func) def updateProject(self, project): if project: self.project = project self.nmrProject = project.currentNmrProject or self.project.newNmrProject( name=project.name) self.updateShiftListPulldown() self.updateChainPulldown() self.updateDangleStorePulldown() self.updateConstrSetPulldown() self.updateEnsemblePulldown() self.updatePredictionMatrixAfter() def makeDangleInput(self, filename, chain, shiftList): if (not chain) or (not shiftList): return residues = chain.sortedResidues() seq = '' for residue in residues: if residue.molResidue.chemComp.code1Letter: seq += residue.molResidue.chemComp.code1Letter else: seq += 'X' res_0 = residues[0].seqId fopen = open(filename, 'w') fopen.write('<entry>\n') fopen.write('\t<res_0>%d</res_0>\n' % res_0) fopen.write('\t<seq_1>%s</seq_1>\n' % seq) fopen.write('\t<chain>%s</chain>\n' % chain.code) fopen.write('\t<cs_data>\n') fopen.write('\t\t<!-- res_id res_name atom_name chemical_shift -->') numShift = 0 for residue in residues: for atom in residue.atoms: atomSet = atom.getAtomSet() shifts = getAtomSetShifts(atomSet, shiftList=shiftList) if (len(shifts) == 0): continue # to average ambiguous chemical shifts ??????????????????????????? value = 0. for shift in shifts: value += shift.value value = value / float(len(shifts)) at_name = atom.name res_name = residue.ccpCode res_num = residue.seqId fopen.write('\n\t\t%5s\t%s\t%-4s\t%.3f' % (res_num, res_name, at_name, value)) numShift += 1 fopen.write('\n\t</cs_data>\n') fopen.write('</entry>\n') fopen.close() return numShift def deleteRun(self): if self.dangleStore: msg = 'Really delete DANGLE run "%s"?' % self.dangleStore.name if showOkCancel('Confirm', msg, parent=self): self.dangleStore.delete() self.dangleStore = None self.dangleChain = None self.updatePredictionMatrix() self.updateDangleStorePulldown() def runDangle(self): chain = self.chain shiftList = self.shiftList if (not chain) or (not shiftList): showError('Cannot Run DANGLE', 'Please specify a chain and a shift list.', parent=self) return # check if there is a DangleChain available self.checkDangleStore() dangleStore = self.dangleStore if not dangleStore: return dangleChain = dangleStore.findFirstDangleChain(chain=chain) if dangleChain: data = (chain.code, dangleChain.shiftList.serial) msg = 'Predictions for Chain %s using Shift List %d already exist.\nReplace data?' % data if not showYesNo('Replace Data', msg, parent=self): return else: self.dangleChain = dangleChain dangleChain.shiftList = shiftList else: self.dangleChain = dangleStore.newDangleChain(chain=chain, shiftList=shiftList) #dangleStore.packageLocator.repositories[0].url.dataLocation = '/home/msc51/ccpn/NexusTestGI' #dangleStore.packageName = 'cambridge.dangle' repository = dangleStore.packageLocator.repositories[0] array = dangleStore.packageName.split('.') path = os.path.join(repository.url.dataLocation, *array) path = os.path.join(path, dangleStore.name, chain.code) # Dangle_dir/dangleStoreName/chainCode if not os.path.exists(path): os.makedirs(path) self.dangleDir = path inputFile = os.path.join(self.dangleDir, 'dangle_cs.inp') if os.path.isfile(inputFile): os.unlink(inputFile) outputFile = os.path.join(self.dangleDir, 'danglePred.txt') if os.path.isfile(outputFile): os.unlink(outputFile) numShift = self.makeDangleInput(inputFile, chain, shiftList) if not os.path.isfile(inputFile): msg = 'No DANGLE input has been generated.\nPlease check shift lists.' showError('File Does Not Exist', msg, parent=self) return if numShift == 0: msg = 'No shift data in input file.\nPerhaps shifts are not assigned.\nContinue prediction anyway?' if not showYesNo('Empty DANGLE input', msg, parent=self): return rejectThresh = self.rejectPulldown.getObject() # Use the Reference info from the main installation # location must be absolute because DANGLE could be run from anywhere location = os.path.dirname(dangleModule.__file__) progressBar = ProgressBar(self) self.update_idletasks() dangle = Dangle(location, inputFile=inputFile, outputDir=self.dangleDir, reject=rejectThresh, angleOnly=False, progressBar=progressBar, writePgm=False) #self.dangleDir = '/home/msc51/nexus/gItest/DanglePred/' #outputFile = '/home/msc51/nexus/gItest/DanglePred/danglePred.txt' predictions = dangle.predictor.predictions gleScores = dangle.predictor.gleScores self.readPredictions(predictions, gleScores) self.updatePredictionMatrix() def readPredictions(self, predictions, gleScores): progressBar = ProgressBar(self, text='Reading DANGLE predictions') progressBar.total = len(predictions) - 2 # 2 header lines residues = self.dangleChain.chain.sortedResidues() getDangleResidue = self.dangleChain.findFirstDangleResidue newDangleResidue = self.dangleChain.newDangleResidue for residue in residues: seqId = residue.seqId prediction = predictions.get(seqId) if prediction is None: continue gleMatrix = gleScores[seqId] progressBar.increment() #resNum, resName = prediction[:2]; numIsland = prediction[2] ss = prediction[10] angles = [min(179.9999, a) for a in prediction[3:10]] phi, phiUpper, phiLower, psi, psiUpper, psiLower, omega = angles # Normalise to max maxVal = max(gleMatrix) gleMatrix = [ max(0, int(val / maxVal * 65535)) / 65535.0 for val in gleMatrix ] dangleResidue = getDangleResidue(residue=residue) if not dangleResidue: dangleResidue = newDangleResidue( phiPsiLikelihoodMatrix=gleMatrix, residue=residue) else: dangleResidue.phiPsiLikelihoodMatrix = gleMatrix dangleResidue.numIslands = numIsland dangleResidue.phiValue = phi dangleResidue.phiUpper = phiUpper dangleResidue.phiLower = phiLower dangleResidue.psiValue = psi dangleResidue.psiUpper = psiUpper dangleResidue.psiLower = psiLower dangleResidue.omegaValue = omega dangleResidue.secStrucCode = ss progressBar.destroy() def readPredictionFile(self, filename, chain): try: fopen = open(filename, 'r') except: showError('File Reading Error', 'DANGLE prediction file %s cannot be open.' % filename, parent=self) return lines = fopen.readlines() progressBar = ProgressBar(self, text='Reading DANGLE predictions') progressBar.total = len(lines) - 2 # 2 header lines lines = lines[2:] for line in lines: progressBar.increment() if (line == '\n'): continue array = line.split() # keep everything as string resNum = int(array[0]) resName = array[1] numIsland = int(array[2]) phi = array[3] phiUpper = array[4] phiLower = array[5] psi = array[6] psiUpper = array[7] psiLower = array[8] omega = array[9] ss = array[10] if (phi == 'None'): phi = None else: phi = float(phi) if (psi == 'None'): psi = None else: psi = float(psi) if (omega == 'None'): omega = None else: omega = float(omega) if omega == 180: omega = 179.9 if (phiUpper == 'None'): phiUpper = None else: phiUpper = float(phiUpper) if (phiLower == 'None'): phiLower = None else: phiLower = float(phiLower) if (psiUpper == 'None'): psiUpper = None else: psiUpper = float(psiUpper) if (psiLower == 'None'): psiLower = None else: psiLower = float(psiLower) if (ss == 'None'): ss = None path = os.path.join(self.dangleDir, 'Res_%d.pgm' % resNum) gleMatrix = self.readGLE(path) residue = chain.findFirstResidue(seqId=int(resNum)) dangleResidue = self.dangleChain.findFirstDangleResidue( residue=residue) if not dangleResidue: dangleResidue = self.dangleChain.newDangleResidue( phiPsiLikelihoodMatrix=gleMatrix, residue=residue) else: dangleResidue.phiPsiLikelihoodMatrix = gleMatrix dangleResidue.numIslands = numIsland dangleResidue.phiValue = phi dangleResidue.phiUpper = phiUpper dangleResidue.phiLower = phiLower dangleResidue.psiValue = psi dangleResidue.psiUpper = psiUpper dangleResidue.psiLower = psiLower dangleResidue.omegaValue = omega dangleResidue.secStrucCode = ss # Delete temp pgm files to save space once data is in CCPN os.unlink(path) fopen.close() progressBar.destroy() def readGLE(self, gleFile): if not os.path.isfile(gleFile): msg = 'No scorogram Res_%d.pgm\nin directory %s.' % ( resNum, self.dangleDir) showError('File Reading Error', msg, parent=self) return None fopen = open(gleFile, 'r') lines = fopen.readlines() dims = lines[2].split() lines = lines[4:] fopen.close() # only read the top left corner of a 10X10 square bin # all readings in the same bin are identical binSize = 10 matrix = [] for j in range(36): x = j * binSize * binSize * 36 for i in range(36): y = i * binSize v = int(lines[x + y].strip()) matrix.append(v) maxVal = float(max(matrix)) for i in range(len(matrix)): matrix[i] = matrix[i] / maxVal return matrix def getPhiPsiPredictions(self): #if self.dangleChain: # dResidues = self.dangleChain.dangleResidues dResidues = self.predictionMatrix.objectList phiData = [] psiData = [] for dResidue in dResidues: resNum = dResidue.residue.seqCode phi = dResidue.phiValue psi = dResidue.psiValue phiData.append((resNum, phi)) psiData.append((resNum, psi)) return (phiData, psiData) def clearSelected(self): for dangleResidue in self.predictionMatrix.currentObjects: dangleResidue.numIslands = None dangleResidue.phiValue = None dangleResidue.psiValue = None dangleResidue.omegaValue = None dangleResidue.phiUpper = None dangleResidue.phiLower = None dangleResidue.psiUpper = None dangleResidue.psiLower = None dangleResidue.secStrucCode = None self.updatePredictionMatrixAfter() def storeSecondaryStructure(self): if not self.dangleChain: return getSpinSystem = self.nmrProject.findFirstResonanceGroup newSpinSystem = self.nmrProject.newResonanceGroup n = 0 for dangleResidue in self.dangleChain.dangleResidues: ssCode = dangleResidue.secStrucCode if not ssCode: continue residue = dangleResidue.residue if not residue: continue spinSystem = getSpinSystem(residue=residue) if not spinSystem: spinSystem = newSpinSystem(residue=residue, ccpCode=residue.ccpCode) spinSystem.secStrucCode = ssCode n += 1 showInfo('Info', 'Stored secondary structure types for %d residues.' % n, parent=self) def storeDihedralConstraints(self): if not self.dangleChain: return # make a new dihedralConstraintList head = self.constraintSet if not head: head = self.project.newNmrConstraintStore( nmrProject=self.nmrProject) self.constraintSet = head chain = self.dangleChain.chain shiftList = self.dangleChain.shiftList name = 'DANGLE Chain %s:%s ShiftList %d' % ( chain.molSystem.code, chain.code, shiftList.serial) constraintList = head.newDihedralConstraintList(name=name, measureListSerials=[ shiftList.serial, ]) # traverse the sequence and make appropriate constraint objects residues = chain.sortedResidues() for residue in residues: # Ensure we have atomSets etc getResidueMapping(residue) residueList = [(dr.residue.seqCode, dr.residue, dr) for dr in self.dangleChain.dangleResidues] residueList.sort() cnt = 0 for seqCode, residue, dangleResidue in residueList: phi = dangleResidue.phiValue psi = dangleResidue.psiValue if (phi is None) and (psi is None): continue # Use below functions because residues may not be sequentially numbered prevRes = getLinkedResidue(residue, 'prev') nextRes = getLinkedResidue(residue, 'next') if (prevRes is None) or (nextRes is None): continue C__1 = prevRes.findFirstAtom(name='C') # C (i-1) N_0 = residue.findFirstAtom(name='N') # N (i) CA_0 = residue.findFirstAtom(name='CA') # CA(i) C_0 = residue.findFirstAtom(name='C') # N (i) N_1 = nextRes.findFirstAtom(name='N') # C (i+1) # get fixedResonances fixedResonances = [] for atom in (C__1, N_0, CA_0, C_0, N_1): atomSet = atom.atomSet if atomSet.resonanceSets: resonance = atomSet.findFirstResonanceSet( ).findFirstResonance() else: # make new resonance if not atom.chemAtom: print 'no chem atom' ic = atom.chemAtom.elementSymbol if (ic == 'C'): ic = '13' + ic elif (ic == 'N'): ic = '15' + ic resonance = self.nmrProject.newResonance(isotopeCode=ic) assignAtomsToRes([ atomSet, ], resonance) fixedResonances.append(getFixedResonance(head, resonance)) # make dihedralConstraints phiResonances = (fixedResonances[0], fixedResonances[1], fixedResonances[2], fixedResonances[3]) phiConstraint = constraintList.newDihedralConstraint( resonances=phiResonances) psiResonances = (fixedResonances[1], fixedResonances[2], fixedResonances[3], fixedResonances[4]) psiConstraint = constraintList.newDihedralConstraint( resonances=psiResonances) # make constraint items if phi is not None: phiConstraint.newDihedralConstraintItem( targetValue=phi, upperLimit=dangleResidue.phiUpper, lowerLimit=dangleResidue.phiLower) cnt += 1 if psi is not None: psiConstraint.newDihedralConstraintItem( targetValue=psi, upperLimit=dangleResidue.psiUpper, lowerLimit=dangleResidue.psiLower) cnt += 1 showInfo('Success', 'DANGLE has generated %d dihedral restraints.' % cnt, parent=self) def loadGLEs(self, dRes, row, col): residue = dRes.residue title = '%d %s' % (residue.seqCode, residue.ccpCode) self.fillGlePlot(self.plot, dRes.phiPsiLikelihoodMatrix, title) prevDangleRes = self.getDangleResidue(dRes, 'prev') if prevDangleRes: self.fillGlePlot(self.prevPlot, prevDangleRes.phiPsiLikelihoodMatrix) else: self.fillGlePlot(self.prevPlot, [0] * 1296) # blank nextDangleRes = self.getDangleResidue(dRes, 'next') if nextDangleRes: self.fillGlePlot(self.nextPlot, nextDangleRes.phiPsiLikelihoodMatrix) else: self.fillGlePlot(self.nextPlot, [0] * 1296) # blank self.updatePhiPsi(dRes.residue) def fillGlePlot(self, plot, gleMatrix, title=None): scaleCol = plot.scaleColorQuick if self.colorScheme == 'black': plot.nullColor = '#000000' else: plot.nullColor = '#FFFFFF' itemconf = plot.canvas.itemconfigure matrix = plot.matrix for j in range(36): for i in range(36): v = gleMatrix[j * 36 + i] #if (v < 0.005): # color = plot.nullColor #else: color = self.getPlotColor(v) item = matrix[i][j] if plot.binWidth < 7: itemconf(item, fill=color, outline=color) elif plot.binWidth < 12: itemconf(item, fill=color, outline=scaleCol(color, 0.9)) else: itemconf(item, fill=color, outline=scaleCol(color, 0.8)) if title: itemconf(plot.title, text=title) def getDangleResidue(self, dRes, direction): # return a DangleResidue object located offset-residue away from dRes in sequence # Use below function to guard against non-sequentially numbered residues # the below function follows bonds, but uses a cache for speed residue = getLinkedResidue(dRes.residue, direction) if residue and self.dangleChain: return self.dangleChain.findFirstDangleResidue(residue=residue) def showPrevious(self): if not self.dangleResidue: return prevDangleResidue = self.getDangleResidue(self.dangleResidue, 'prev') if not prevDangleResidue: return self.predictionMatrix.selectObject(prevDangleResidue) #self.dangleResidue = prevDangleResidue #self.loadGLEs(self.dangleResidue, None, None) #self.predictionMatrix.currentObject = self.dangleResidue #self.predictionMatrix.hilightObject(self.predictionMatrix.currentObject) def showNext(self): if not self.dangleResidue: return nextDangleResidue = self.getDangleResidue(self.dangleResidue, 'next') if not nextDangleResidue: return self.predictionMatrix.selectObject(nextDangleResidue) #self.dangleResidue = nextDangleResidue #self.loadGLEs(self.dangleResidue, None, None) #self.predictionMatrix.currentObject = self.dangleResidue #self.predictionMatrix.hilightObject(self.predictionMatrix.currentObject) def updatePhiPsi(self, residue): if self.ensemble: phiPsiAccept = [] plotObjects = [] colors = [] cChain = self.ensemble.findFirstCoordChain(code=residue.chain.code) if cChain: cResidue = cChain.findFirstResidue(residue=residue) if cResidue: for model in self.ensemble.models: phiPsiAccept.append(self.getPhiPsi(cResidue, model)) plotObjects.append((cResidue, model)) colors.append(ENSEMBLE_COLOR) if self.colorScheme == 'rainbow': # default grey circles self.plot.updateObjects(phiPsiAccList=phiPsiAccept, objectList=plotObjects) else: # bright green circles self.plot.updateObjects(phiPsiAccList=phiPsiAccept, objectList=plotObjects, colors=colors) def getPhiPsi(self, residue, model=None): phi, psi = getResiduePhiPsi(residue, model=model) return (phi, psi, 1) def getPlotColor(self, i, maxInt=255): mode = self.colorScheme if mode == 'rainbow': if (i == 0): return '#%02x%02x%02x' % (255, 255, 255) # white bg elif (i > 0.75): red = 1 green = (1 - i) / 0.25 blue = 0 elif (i > 0.5): red = (i - 0.5) / 0.25 green = 1 blue = 0 elif (i > 0.25): red = 0 green = 1 blue = (0.5 - i) / 0.25 else: red = 0 green = i / 0.25 blue = 1 return '#%02x%02x%02x' % (red * maxInt, green * maxInt, blue * maxInt) """ elif mode == 'black': if i > 0.5: red = i green = 1 - i blue = 1 - i else: v = 0.1 + (0.9 * i) red = v green = v blue = v elif mode == 'white': if i > 0.5: red = i green = 1 - i blue = 1 - i else: v = 1.0 - (0.9 * i) red = v green = v blue = v return '#%02x%02x%02x' % (red*maxInt, green*maxInt, blue*maxInt) """ # default : red to black if (i == 0): return '#%02x%02x%02x' % (255, 255, 255) # white bg return '#%02x%02x%02x' % (((1 - i) * 255), 0, 0) def updatePredictionMatrixAfter(self, index=None, text=None): if self.chain and self.shiftList and self.dangleStore: self.dangleChain = self.dangleStore.findFirstDangleChain( chain=self.chain, shiftList=self.shiftList) else: self.dangleChain = None self.after_idle(self.updatePredictionMatrix) #if showYesNo('Not Found','No data for Chain %s in Dangle Run %s.\nMake prediction for this chain?' % (self.chain.code, text), parent=self): # self.runDangle() def updatePredictionMatrix(self): shiftList = self.shiftList objectList = [] textMatrix = [] colorMatrix = [] if self.dangleChain: residueList = [(dr.residue.seqCode, dr.residue, dr) for dr in self.dangleChain.dangleResidues] residueList.sort() else: # Chow blank table residueList = [] for seqCode, residue, dRes in residueList: objectList.append(dRes) phi = dRes.phiValue psi = dRes.psiValue ss = dRes.secStrucCode atomNames = [] for atomName in BACKBONE_ATOMS: atom = residue.findFirstAtom(name=atomName) if not atom: continue atomSet = atom.atomSet if atomSet: shifts = getAtomSetShifts(atomSet, shiftList=shiftList) if shifts: atomNames.append(atomName) atomNames.sort() atomNames = ' '.join(atomNames) textMatrix.append((seqCode, residue.ccpCode, dRes.numIslands, ss, phi, psi, dRes.phiUpper, dRes.phiLower, dRes.psiUpper, dRes.psiLower, atomNames)) if (phi is None) and (psi is None): colorMatrix.append(INACTIVE_COLORS) elif dRes.numIslands >= 5: colorMatrix.append(BAD_COLORS) else: colorMatrix.append(GOOD_COLORS) self.predictionMatrix.update(textMatrix=textMatrix, objectList=objectList, colorMatrix=colorMatrix) def selectCell(self, dRes, row, col): self.dangleResidue = dRes self.row = row self.col = col self.loadGLEs(dRes, row, col) def setFloatEntry(self, event): index = self.col - 4 # index of attribute to set in the EDIT_ATTRS list value = self.floatEntry.get() if value is not None: setattr(self.dangleResidue, EDIT_ATTRS[index], value) self.updatePredictionMatrixAfter() def getFloatEntry(self, dangleResidue): if dangleResidue: index = self.col - 4 # index of attribute to set in the EDIT_ATTRS list self.floatEntry.set(getattr(dangleResidue, EDIT_ATTRS[index])) def changeChain(self, chain): if chain is not self.chain: self.chain = chain self.updateEnsemblePulldown( ) # Ensembles are filtered by chains molSystem self.updatePredictionMatrixAfter() def changeShiftList(self, shiftList): if shiftList is not self.shiftList: self.shiftList = shiftList self.updatePredictionMatrixAfter() def changeEnsemble(self, ensemble): self.ensemble = ensemble def changeConstraintSet(self, constraintSet): if constraintSet is not self.constraintSet: self.constraintSet = constraintSet def changeDangleStore(self, dangleStore): if self.dangleStore is not dangleStore: self.dangleStore = dangleStore if dangleStore: self.dangleChain = dangleStore.findFirstDangleChain() self.chain = self.dangleChain.chain self.shiftList = self.dangleChain.shiftList else: self.dangleChain = None self.updateChainPulldown() self.updateShiftListPulldown() self.updateEnsemblePulldown( ) # Ensembles are filtered by chains molSystem self.updatePredictionMatrixAfter() def checkDangleStore(self): if not self.dangleStore: N = len(self.project.dangleStores) + 1 name = askString('Request', 'Dangle Run Name:', 'Run%d' % N, parent=self) if not name: return None for character in whitespace: if character in name: showWarning('Failure', 'Name cannot contain whitespace', parent=self) return None if self.project.findFirstDangleStore(name=name): showWarning('Failure', 'Name already used', parent=self) return None self.dangleStore = self.project.newDangleStore(name=name) self.updateDangleStorePulldown() def updateChainPulldown(self, obj=None): index = 0 names = [] chains = [] chain = self.chain for molSystem in self.project.molSystems: msCode = molSystem.code for chainA in molSystem.chains: residues = chainA.residues if not residues: continue for residue in residues: # Must have at least one protein residue if residue.molType == 'protein': names.append('%s:%s' % (msCode, chainA.code)) chains.append(chainA) break if chains: if chain not in chains: chain = chains[0] index = chains.index(chain) else: chain = None if chain is not self.chain: self.chain = chain self.updatePredictionMatrixAfter() self.chainPulldown.setup(names, chains, index) def updateShiftListPulldown(self, obj=None): index = 0 names = [] shiftLists = getShiftLists(self.nmrProject) if shiftLists: if self.shiftList not in shiftLists: self.shiftList = shiftLists[0] index = shiftLists.index(self.shiftList) names = ['%s:%d' % (sl.name, sl.serial) for sl in shiftLists] else: self.shiftList = None self.shiftListPulldown.setup(names, shiftLists, index) def updateDangleStorePulldown(self): names = [ '<New>', ] dangleStores = [ None, ] for dangleStore in self.project.sortedDangleStores(): names.append(dangleStore.name) dangleStores.append(dangleStore) if self.dangleStore not in dangleStores: self.dangleStore = dangleStores[-1] index = dangleStores.index(self.dangleStore) self.dangleStorePulldown.setup(names, dangleStores, index) def updateEnsemblePulldown(self, obj=None): index = 0 names = [ '<None>', ] ensembles = [ None, ] if self.chain: molSystem = self.chain.molSystem for ensemble in molSystem.sortedStructureEnsembles(): names.append('%s:%d' % (molSystem.code, ensemble.ensembleId)) ensembles.append(ensemble) if self.ensemble not in ensembles: self.ensemble = ensembles[0] index = ensembles.index(self.ensemble) self.ensemblePulldown.setup(names, ensembles, index) def updateConstrSetPulldown(self, obj=None): names = [ '<New>', ] constraintSets = [ None, ] # Use below later, once API speed/loading is improved # for constraintSet in self.nmrProject.sortedNmrConstraintStores(): for constraintSet in self.project.sortedNmrConstraintStores(): names.append('%d' % constraintSet.serial) constraintSets.append(constraintSet) if self.constraintSet not in constraintSets: self.constraintSet = constraintSets[0] index = constraintSets.index(self.constraintSet) self.constrSetPulldown.setup(names, constraintSets, index) def try1(self): if not self.project: return ccpCodes = [ 'Ala', 'Cys', 'Asp', 'Glu', 'Phe', 'Gly', 'His', 'Ile', 'Lys', 'Leu', 'Met', 'Asn', 'Gln', 'Arg', 'Ser', 'Thr', 'Val', 'Trp', 'Tyr', 'Pro' ] atomNames = ['HA', 'CA', 'CB', 'C', 'N'] molType = 'protein' for ccpCode in ccpCodes: for atomName in atomNames: chemAtomNmrRef = getChemAtomNmrRef(self.project, atomName, ccpCode, molType) mean = chemAtomNmrRef.meanValue sd = chemAtomNmrRef.stdDev print '%5s%5s %.3f %.3f' % (ccpCode, atomName, mean, sd)
class SecStructurePredictPopup(BasePopup): """ **Predict Protein Secondary Structure** This popup window is designed to allow the prediction of secondary structure for a protein chain given chemical shifts, using the (external) program D2D. The Options to select are the Chain and the Shift List, for which the prediction is then made. The Secondary Structure Predictions table lists the residues in the chain. For each residue the residue number, residue type and current secondary structure set for that residue is given. The remaining columns are for the predictions made by D2D, and of course are only filled in once D2D is run. The predicted secondary structure is listed first, followed by the probability of that residue being Helix, Beta, Coil or PPII (the predicted secondary structure will be specified by the maximum of these). To run the prediction click on the "Run D2D Prediction!" button. This does not store this information in the project. To do that you have to click on the "Commit Predicted Secondary Structure" button. **Caveats & Tips** The predicted secondary structure cell is coloured red if the prediction is unreliable. Unreliable predictions are not stored with the "Commit" button but all reliable ones are. If you need to edit the secondary structure for a residue then use the Secondary Structure Chart: .. _Secondary Structure Chart: SecStructureGraphPopup.html **References** The D2D programme: http://www-vendruscolo.ch.cam.ac.uk/d2D/index.php *C. Camilloni, A. De Simone, W. Vranken and M. Vendruscolo. Determination of Secondary Structure Populations in Disordered States of Proteins using NMR Chemical Shifts. Biochemistry 2012, 51: 2224-2231 """ def __init__(self, parent, *args, **kw): self.chain = None self.shiftList = None self.predictionDict = {} BasePopup.__init__(self, parent=parent, title='Structure : Predict Secondary Structure') def body(self, guiFrame): self.geometry('700x500') guiFrame.expandGrid(1, 0) row = 0 # TOP LEFT FRAME frame = LabelFrame(guiFrame, text='Options') frame.grid(row=row, column=0, sticky='nsew') frame.columnconfigure(5, weight=1) label = Label(frame, text='Chain') label.grid(row=0, column=0, sticky='w') self.chainPulldown = PulldownList( frame, callback=self.changeChain, tipText='Choose the molecular system chain to make predictions for' ) self.chainPulldown.grid(row=0, column=1, sticky='w') label = Label(frame, text='Shift List') label.grid(row=0, column=2, sticky='w') self.shiftListPulldown = PulldownList( frame, callback=self.changeShiftList, tipText='Select the shift list to take input chemical shifts from') self.shiftListPulldown.grid(row=0, column=3, sticky='w') row += 1 # BOTTOM LEFT FRAME frame = LabelFrame(guiFrame, text='Secondary Structure Predictions') frame.grid(row=row, column=0, sticky='nsew') frame.grid_columnconfigure(0, weight=1) frame.grid_rowconfigure(0, weight=1) tipTexts = ('Residue number in chain', 'Residue type code', 'Current stored secondary structure code', 'Predicted secondary structure code') + SEC_STRUC_TIPS headingList = ('Res\nNum', 'Res\nType', 'Current\nSS', 'Predicted\nSS') + SEC_STRUC_KEYS n = len(headingList) editWidgets = n * [None] editGetCallbacks = n * [None] editSetCallbacks = n * [None] self.predictionMatrix = ScrolledMatrix( frame, headingList=headingList, tipTexts=tipTexts, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks) self.predictionMatrix.grid(row=0, column=0, sticky='nsew') row += 1 tipTexts = [ 'Run the D2D method to predict secondary structure', 'Store the secondary structure predictions in the CCPN project' ] texts = [ 'Run D2D Prediction!', 'Commit Predicted\nSecondary Structure' ] commands = [self.runD2D, self.storeSecondaryStructure] self.buttonList = createDismissHelpButtonList(guiFrame, texts=texts, commands=commands, help_url=self.help_url, expands=True, tipTexts=tipTexts) self.buttonList.grid(row=row, column=0, columnspan=2, sticky='ew') self.update() self.notify(self.registerNotify) def destroy(self): self.notify(self.unregisterNotify) BasePopup.destroy(self) def notify(self, notifyfunc): for func in ('__init__', 'delete'): notifyfunc(self.updateChainPulldown, 'ccp.molecule.MolSystem.Chain', func) for func in ('__init__', 'delete', 'setName'): notifyfunc(self.updateShiftListPulldown, 'ccp.nmr.Nmr.ShiftList', func) for func in ('setSecStrucCode', ): notifyfunc(self.updatePredictionMatrixAfter, 'ccp.nmr.Nmr.ResonanceGroup', func) def update(self): self.updateShiftListPulldown() self.updateChainPulldown() self.updatePredictionMatrixAfter() def runD2D(self): chain = self.chain shiftList = self.shiftList if (not chain) or (not shiftList): showError('Cannot Run D2D', 'Please specify a chain and a shift list.', parent=self) return self.predictionDict[chain] = runD2D(chain, shiftList) self.updatePredictionMatrix() def storeSecondaryStructure(self): if not self.chain: return getSpinSystem = self.nmrProject.findFirstResonanceGroup newSpinSystem = self.nmrProject.newResonanceGroup predDict = self.predictionDict.get(self.chain, {}) n = 0 for residue in predDict: (ssCode, isReliable, probabilityDict) = predDict[residue] if isReliable: spinSystem = getSpinSystem(residue=residue) if not spinSystem: spinSystem = newSpinSystem(residue=residue, ccpCode=residue.ccpCode) spinSystem.secStrucCode = ssCode n += 1 showInfo('Info', 'Stored secondary structure types for %d residues.' % n, parent=self) def updatePredictionMatrixAfter(self, index=None, text=None): self.after_idle(self.updatePredictionMatrix) def updatePredictionMatrix(self): objectList = [] textMatrix = [] colorMatrix = [] n = len(SEC_STRUC_KEYS) chain = self.chain if chain: predDict = self.predictionDict.get(chain, {}) getSpinSystem = self.nmrProject.findFirstResonanceGroup for residue in chain.sortedResidues(): spinSystem = getSpinSystem(residue=residue) currentSsCode = spinSystem and spinSystem.secStrucCode data = [residue.seqCode, residue.ccpCode, currentSsCode] colors = 3 * [None] if residue in predDict: (ssCode, isReliable, probabilityDict) = predDict[residue] data.append(ssCode) if isReliable: colors.append(None) else: colors.append('#FF3333') for key in SEC_STRUC_KEYS: data.append(probabilityDict[key]) colors.extend(n * [None]) else: data.extend((n + 1) * [None]) colors.extend((n + 1) * [None]) textMatrix.append(data) objectList.append(residue) colorMatrix.append(colors) self.predictionMatrix.update(textMatrix=textMatrix, objectList=objectList, colorMatrix=colorMatrix) def changeChain(self, chain): if chain is not self.chain: self.chain = chain self.updatePredictionMatrixAfter() def changeShiftList(self, shiftList): if shiftList is not self.shiftList: self.shiftList = shiftList self.updatePredictionMatrixAfter() def updateChainPulldown(self, obj=None): index = 0 names = [] chains = [] chain = self.chain for molSystem in self.project.molSystems: msCode = molSystem.code for chainA in molSystem.chains: residues = chainA.residues if not residues: continue for residue in residues: # Must have at least one protein residue if residue.molType == 'protein': names.append('%s:%s' % (msCode, chainA.code)) chains.append(chainA) break if chains: if chain not in chains: chain = chains[0] index = chains.index(chain) else: chain = None if chain is not self.chain: self.chain = chain self.updatePredictionMatrixAfter() self.chainPulldown.setup(names, chains, index) def updateShiftListPulldown(self, obj=None): index = 0 names = [] shiftLists = getShiftLists(self.nmrProject) if shiftLists: if self.shiftList not in shiftLists: self.shiftList = shiftLists[0] index = shiftLists.index(self.shiftList) names = ['%s:%d' % (sl.name, sl.serial) for sl in shiftLists] else: self.shiftList = None self.shiftListPulldown.setup(names, shiftLists, index)
class SelectionListPopup(TemporaryBasePopup): def __init__(self, parent, selectionList, title = 'Select', text = 'Select', topText = None, dismissText = None, selected = None, selectionDict = None, urlFile = None, dismissButton = True, modal = False): self.selectionList = selectionList self.selectionDict = selectionDict self.text = text self.dismissButton = dismissButton if dismissButton: if dismissText: self.dismissText = dismissText else: self.dismissText = 'dismiss' self.topText = topText self.isSelected = None if not selected: self.selectedIndex = 0 else: self.selectedIndex = self.selectionList.index(selected) if urlFile: self.help_url = joinPath(getHelpUrlDir(),urlFile + '.html') else: self.help_url = None TemporaryBasePopup.__init__(self,parent = parent, title = title, modal = modal, transient=True) def body(self, master): # # Popup window # row = 0 if self.topText: label = Label(master, text= self.topText) label.grid(row=row, column=0, columnspan = 2, sticky=Tkinter.EW) row = row + 1 label = Label(master, text= self.text) label.grid(row=row, column=0, sticky=Tkinter.EW) self.menu = PulldownList(master, texts = self.selectionList, index = self.selectedIndex) self.menu.grid(row=row, column=1, sticky=Tkinter.E, ipadx = 20) row = row + 1 texts = [ 'OK' ] commands = [ self.ok ] # This calls 'ok' in BasePopup, this then calls 'apply' in here if self.dismissButton: buttons = createDismissHelpButtonList(master, texts=texts, commands=commands, dismiss_text = self.dismissText, help_url=self.help_url) else: buttons = createHelpButtonList(master, texts=texts, commands=commands, help_url=self.help_url) buttons.grid(row=row, column=0, columnspan = 3) def apply(self): self.isSelected = self.menu.getText() if self.selectionDict: self.selection = self.selectionDict[self.isSelected] else: self.selection = self.isSelected return True