class AssignMentTransferTab(object):
    '''the tab in the GUI where assignments
       can be transferred in bulk to the ccpn analysis
       project. A difference is made between two types
       of assignments:
           1) spin systems to residues, which also
              implies resonanceSets to atomSets.
           2) resonances to peak dimensions.
       The user is able to configure which assignments
       should be transferred to the project.

      Attributes:

          guiParent: gui object this tab is part of.

          frame: the frame in which this element lives.

          dataModel(src.cython.malandro.DataModel): dataModel
              object describing the assignment proposed by
              the algorithm.

          selectedSolution (int): The index of the solution/run
              that is used asa the template to make the assignments.

          resonanceToDimension (bool): True if resonances should
              be assigned to peak dimensions. False if not.

          spinSystemToResidue (bool): True if spin system to
              residue assignment should be carried out.

          minScore (float): The minimal score of a spin system
              assignment to a residue to be allowed
              to transfer this assignment to the project

          intra (bool): True if intra-residual peaks should be
              assigned.

          sequential (bool): True if sequential peaks should be
              assigned.

          noDiagonal (bool): If True, purely diagonal peaks are
              ignored during the transfer of assignments.

          allSpectra (bool): If True, all spectra will be assigned.
              If False, one specified spectrum will be assigned.

          spectrum (src.cython.malandro.Spectrum): The spectrum
              that should be assigned.
    '''

    def __init__(self, parent, frame):
        '''Init. args: parent: the guiElement that this
                               tab is part of.
                       frame:  the frame this part of the
                               GUI lives in.
        '''

        self.guiParent = parent
        self.frame = frame

        # Buttons and fields,
        # will be set in body():
        self.peaksCheckButton = None
        self.residuesCheckButton = None
        self.intraCheckButton = None
        self.sequentialCheckButton = None
        self.noDiagonalCheckButton = None
        self.spinSystemTypeSelect = None
        self.minScoreEntry = None
        self.solutionNumberEntry = None
        self.spectrumSelect = None
        self.spectraPullDown = None
        self.assignedResidueStrategySelect = None
        self.transferButton = None

        # Settings that determine how assignments
        # are transferred to the analysis project:
        self.minScore = 80.0
        self.dataModel = None
        self.spectrum = None
        self.selectedSolution = 1
        self.body()
        self.resonanceToDimension = True
        self.spinSystemToResidue = True
        self.intra = True
        self.sequential = True
        self.noDiagonal = True
        self.allSpectra = True
        self.spinSystemType = 0
        self.strategy = 0


    def body(self):
        '''Describes the body of this tab. It consists
           out of a number of radio buttons, check buttons
           and number entries that allow the user to
           indicate which assignments should be transferred.
        '''

        # self.frame.expandColumn(0)
        self.frame.expandGrid(8, 0)
        self.frame.expandGrid(8, 1)

        typeOfAssignmentFrame = LabelFrame(
            self.frame, text='type of assignment')
        typeOfAssignmentFrame.grid(row=0, column=0, sticky='nesw')
        # typeOfAssignmentFrame.expandGrid(0,5)

        peakSelectionFrame = LabelFrame(
            self.frame, text='which peaks to assign')
        peakSelectionFrame.grid(row=0, column=1, sticky='nesw', rowspan=2)

        spinSystemSelectionFrame = LabelFrame(self.frame,
                                              text='Which spin-systems to use')
        spinSystemSelectionFrame.grid(row=2, column=0, sticky='nesw')

        tipText = 'What to do when a residue has already a spin system assigned to it.'
        assignedResidueFrame = LabelFrame(self.frame,
                                          text='if residue already has spin-system',
                                          tipText=tipText)
        assignedResidueFrame.grid(row=2, column=1, sticky='nesw')

        spectrumSelectionFrame = LabelFrame(self.frame, text='spectra')
        spectrumSelectionFrame.grid(row=1, column=0, sticky='nesw')

        row = 0

        Label(typeOfAssignmentFrame,
              text='Resonances to Peak Dimensions',
              grid=(row, 0))
        self.peaksCheckButton = CheckButton(typeOfAssignmentFrame,
                                            selected=True,
                                            grid=(row, 1))

        row += 1

        Label(typeOfAssignmentFrame,
              text='SpinSystems to Residues',
              grid=(row, 0))
        self.residuesCheckButton = CheckButton(
            typeOfAssignmentFrame, selected=True, grid=(row, 1))

        row = 0

        Label(peakSelectionFrame, text='Intra-Residual', grid=(row, 0))
        self.intraCheckButton = CheckButton(
            peakSelectionFrame, selected=True, grid=(row, 1))

        row += 1

        Label(peakSelectionFrame, text='Sequential', grid=(row, 0))
        self.sequentialCheckButton = CheckButton(
            peakSelectionFrame, selected=True, grid=(row, 1))

        row += 1

        Label(peakSelectionFrame,
              text='Do not assign diagonal peaks',
              grid=(row, 0))
        self.noDiagonalCheckButton = CheckButton(
            peakSelectionFrame, selected=True, grid=(row, 1))

        entries = ['Only assigned spin systems',
                   'All that have a score of at least: ',
                   'User Defined',
                   'Solution number:']
        tipTexts = ['Only assign resonances of spin systems that already have a sequential assignment for the assignment of peak dimensions. Spin system to residue assignment is not relevant in this case.',
                    'Assign all spin systems that have a score of at least a given percentage. 50% or lower is not possible, because than spin systems might have to be assigned to more than 1 residue, which is impossible.',
                    "As defined in the lower row of buttons in the 'results' tab.",
                    'One of the single solutions of the annealing.']
        self.spinSystemTypeSelect = RadioButtons(spinSystemSelectionFrame,
                                                 entries=entries, grid=(0, 0),
                                                 select_callback=None,
                                                 direction=VERTICAL,
                                                 gridSpan=(4, 1),
                                                 tipTexts=tipTexts)

        tipText = 'The minimal amount of colabelling the different nuclei should have in order to still give rise to a peak.'
        self.minScoreEntry = FloatEntry(spinSystemSelectionFrame,
                                        grid=(1, 1), width=7,
                                        text=str(self.minScore),
                                        returnCallback=self.changeMinScore,
                                        tipText=tipText)
        self.minScoreEntry.bind('<Leave>', self.changeMinScore, '+')

        self.solutionNumberEntry = IntEntry(spinSystemSelectionFrame,
                                            grid=(3, 1), width=7, text=1,
                                            returnCallback=self.solutionUpdate,
                                            tipText=tipText)
        self.solutionNumberEntry.bind('<Leave>', self.solutionUpdate, '+')

        #self.solutionPullDown = PulldownList(spinSystemSelectionFrame, None, grid=(3,1), sticky='w')

        entries = ['all spectra', 'only:']
        tipTexts = ['Assign peaks in all the spectra that where selected before the annealing ran.',
                    'Only assign peaks in one particular spectrum. You can of course repeat this multiple times for different spectra.']
        self.spectrumSelect = RadioButtons(spectrumSelectionFrame,
                                           entries=entries,
                                           grid=(0, 0),
                                           select_callback=None,
                                           direction=VERTICAL,
                                           gridSpan=(2, 1), tipTexts=tipTexts)

        self.spectraPullDown = PulldownList(spectrumSelectionFrame,
                                            self.changeSpectrum,
                                            grid=(1, 1), sticky='w')

        entries = ['skip this residue',
                   'de-assign old spin system from residue',
                   'assign, but never merge',
                   'warn to merge']
        tipTexts = ["Don't assign the new spin system to the residue. The residue is not skipped when the old spin system does not contain any resonances",
                    "De-assign old spin system from residue, unless the old spin system is a spin system without any resonances.",
                    "Don't merge any spin systems, merging can be performed later if nescesary in the Resonance --> SpinSystems window.",
                    "Ask to merge individually for each spin system, this might result in clicking on a lot of popups."]
        self.assignedResidueStrategySelect = RadioButtons(assignedResidueFrame,
                                                          entries=entries,
                                                          grid=(0, 0),
                                                          select_callback=None,
                                                          direction=VERTICAL,
                                                          gridSpan=(2, 1),
                                                          tipTexts=tipTexts)

        texts = ['Transfer Assignments']
        commands = [self.transferAssignments]
        self.transferButton = ButtonList(
            self.frame, commands=commands, texts=texts)
        self.transferButton.grid(row=5, column=0, sticky='nsew', columnspan=2)

    def update(self):
        '''Update the nescesary elements in the
           tab. Is called when the algorithm
           has produced possible assignments.
           The only thing that has to be updated
           in practice in this tab is the pulldown
           with spectra.
        '''

        self.dataModel = self.guiParent.connector.results
        self.updateSpectra()

    def setDataModel(self, dataModel):
        '''Here the dataModel, which is the dataModel
           containing the suggested assignments body
           the algorithm, can be set.
        '''

        self.dataModel = dataModel
        self.update()

    def updateSpectra(self, *opt):
        '''Updates the spectra shown in the spectra
           pulldown. These are only the spectra that
           were used by the algorithm. All other spectra
           in the project are not relevant since for those
           no simulated peaks have been matched to real
           peaks.
        '''

        if not self.dataModel:

            return

        spectrum = self.spectrum

        spectra = self.dataModel.getSpectra()

        if spectra:

            names = [spectrum.name for spectrum in spectra]
            index = 0

            if self.spectrum not in spectra:

                self.spectrum = spectra[0]

            else:

                index = spectra.index(self.spectrum)

        self.spectraPullDown.setup(names, spectra, index)

    def changeSpectrum(self, spectrum):
        '''Select a spectum to be assigned.'''

        self.spectrum = spectrum

    def solutionUpdate(self, event=None, value=None):
        '''Select a solution. A solution is a
           one to one mapping of spin systems
           to residues produced by one run of
           the algorithm.
               args: event: event object, this is
                            one of the values the number
                            entry calls his callback
                            function with.
                     value: the index of the solution/run.
        '''

        if not self.dataModel:

            return

        Nsolutions = len(self.dataModel.chain.residues[0].solutions)

        if value is None:

            value = self.solutionNumberEntry.get()

        if value == self.selectedSolution:
            return
        else:
            self.selectedSolution = value
        if value < 1:
            self.solutionNumberEntry.set(1)
            self.selectedSolution = 1
        elif value > Nsolutions:
            self.selectedSolution = Nsolutions
            self.solutionNumberEntry.set(self.selectedSolution)
        else:
            self.solutionNumberEntry.set(self.selectedSolution)

    def fetchOptions(self):
        '''Fetches user set options from the gui in
           one go and stores them in their corresponding
           instance variables.
        '''

        self.resonanceToDimension = self.peaksCheckButton.get()
        self.spinSystemToResidue = self.residuesCheckButton.get()
        self.intra = self.intraCheckButton.get()
        self.sequential = self.sequentialCheckButton.get()
        self.noDiagonal = self.noDiagonalCheckButton.get()
        self.spinSystemType = self.spinSystemTypeSelect.getIndex()
        self.strategy = ['skip', 'remove', 'noMerge', None][
            self.assignedResidueStrategySelect.getIndex()]
        self.allSpectra = [True, False][self.spectrumSelect.getIndex()]

    def changeMinScore(self, event=None):
        '''Set the minimal score for which a spin system
           to residue assignment gets transferred to the
           ccpn analysis project.
        '''

        newMinScore = self.minScoreEntry.get()

        if self.minScore != newMinScore:

            if newMinScore <= 50.0:

                self.minScore = 51.0
                self.minScoreEntry.set(51.0)

            elif newMinScore > 100.0:

                self.minScore = 100.0
                self.minScoreEntry.set(100.0)

            else:

                self.minScore = newMinScore

    def transferAssignments(self):
        '''Transfer assignments to project depending
           on the settings from the GUI.
        '''

        self.fetchOptions()

        if not self.dataModel or (not self.resonanceToDimension and not self.spinSystemToResidue):

            return

        strategy = self.strategy

        lookupSpinSystem = [self.getAssignedSpinSystem,
                            self.getBestScoringSpinSystem,
                            self.getUserDefinedSpinSystem,
                            self.getSelectedSolutionSpinSystem][self.spinSystemType]

        residues = self.dataModel.chain.residues

        spinSystemSequence = [lookupSpinSystem(res) for res in residues]

        ccpnSpinSystems = []
        ccpnResidues = []

        # if self.spinSystemType == 0 it means that it for sure already
        # assigned like this
        if self.spinSystemToResidue and not self.spinSystemType == 0:

            for spinSys, res in zip(spinSystemSequence, residues):

                if spinSys and res:

                    ccpnSpinSystems.append(spinSys.getCcpnResonanceGroup())
                    ccpnResidues.append(res.getCcpnResidue())

            assignSpinSystemstoResidues(ccpnSpinSystems,
                                        ccpnResidues,
                                        strategy=strategy,
                                        guiParent=self.guiParent)

        if self.resonanceToDimension:

            allSpectra = self.allSpectra

            if self.intra:

                for residue, spinSystem in zip(residues, spinSystemSequence):

                    if not spinSystem:

                        continue

                    intraLink = residue.getIntraLink(spinSystem)

                    for pl in intraLink.getPeakLinks():

                        peak = pl.getPeak()

                        if not allSpectra and peak.getSpectrum() is not self.spectrum:

                            continue

                        if not peak:

                            continue

                        resonances = pl.getResonances()

                        if self.noDiagonal and len(set(resonances)) < len(resonances):

                            continue

                        for resonance, dimension in zip(resonances, peak.getDimensions()):

                            ccpnResonance = resonance.getCcpnResonance()
                            ccpnDimension = dimension.getCcpnDimension()
                            assignResToDim(ccpnDimension, ccpnResonance)

            if self.sequential:

                for residue, spinSystemA, spinSystemB in zip(residues,
                                                             spinSystemSequence,
                                                             spinSystemSequence[1:]):

                    if not spinSystemA or not spinSystemB:

                        continue

                    link = residue.getLink(spinSystemA, spinSystemB)

                    for pl in link.getPeakLinks():

                        peak = pl.getPeak()

                        if not allSpectra and peak.getSpectrum() is not self.spectrum:

                            continue

                        if not peak:

                            continue

                        resonances = pl.getResonances()

                        if self.noDiagonal and len(set(resonances)) < len(resonances):

                            continue

                        for resonance, dimension in zip(resonances, peak.getDimensions()):

                            ccpnResonance = resonance.getCcpnResonance()
                            ccpnDimension = dimension.getCcpnDimension()

                            assignResToDim(ccpnDimension, ccpnResonance)

        self.guiParent.resultsTab.update()

    def getAssignedSpinSystem(self, residue):
        '''Get the spinSystem that is assigned in the project
           to a residue.
           args:  residue (src.cython.malandro.Residue)
           return: spinSystem (src.cython.malandro.SpinSystem)
        '''

        ccpCode = residue.ccpCode
        seqCode = residue.getSeqCode()
        spinSystems = self.dataModel.getSpinSystems()[ccpCode]

        ccpnResidue = residue.getCcpnResidue()
        if ccpnResidue:
            assignedResonanceGroups = ccpnResidue.getResonanceGroups()
            if len(assignedResonanceGroups) > 1:
                print 'There is more than one spin system assigned to residue %s, did not know which one to use to assign peaks. Therefor this residue is skipped.' % (seqCode)
                return

            assignedResonanceGroup = ccpnResidue.findFirstResonanceGroup()

            if assignedResonanceGroup:

                for spinSystem in spinSystems:

                    if spinSystem.getSerial() == assignedResonanceGroup.serial:
                        # Just checking to make sure, analysis project could
                        # have changed
                        if not self.skipResidue(residue, spinSystem):

                            return spinSystem

    def getBestScoringSpinSystem(self, residue):
        '''Get the spinSystem that scores the highest,
           i.e. is assigned in most of the runs to the
           given residue.
           args:  residue (src.cython.malandro.Residue)
           return: spinSystem (src.cython.malandro.SpinSystem)
        '''

        solutions = residue.solutions
        weigth = 1.0 / len(solutions)
        score, bestSpinSystem = max([(solutions.count(solution) * weigth * 100.0, solution) for solution in solutions])

        if score >= self.minScore and not bestSpinSystem.getIsJoker() and not self.skipResidue(residue, bestSpinSystem):

            return bestSpinSystem

        return None

    def getUserDefinedSpinSystem(self, residue):
        '''Get the spinSystem that is defined by the user
           (probably in the resultsTab) as the correct
           assignment of the given residue.
           args:  residue (src.cython.malandro.Residue)
           return: spinSystem (src.cython.malandro.SpinSystem)
        '''

        userDefinedSpinSystem = residue.userDefinedSolution

        if userDefinedSpinSystem and not userDefinedSpinSystem.getIsJoker() and not self.skipResidue(residue, userDefinedSpinSystem):

            return userDefinedSpinSystem

        return None

    def getSelectedSolutionSpinSystem(self, residue):
        '''I a solution corresponding to one specific run
           of the algorithm is defined, return which spinSystem
           in that run got assigned to the given residue.
           args:  residue (src.cython.malandro.Residue)
           return: spinSystem (src.cython.malandro.SpinSystem)
        '''

        solutions = residue.solutions

        spinSystem = solutions[self.selectedSolution - 1]

        if not spinSystem.getIsJoker() and not self.skipResidue(residue, spinSystem):

            return spinSystem

        return None

    def skipResidue(self, residue, spinSystem):
        '''One strategy is to skip all residues that
           already have a spin system assignment.
           If that is the case determine whether to
           skip the given residue.
           args: residue (src.cython.malandro.Residue)
                 spinSystem (src.cython.malandro.SpinSystem)
           return: boolean, True if residue should be skipped.
        '''

        if self.strategy == 0:

            assignedGroups = residue.getCcpnResidue().getResonanceGroups()
            assignedSerials = set([spinSys.serial for spinSys in assignedGroups])

            if assignedSerials and spinSystem.getSerial() not in assignedSerials:

                return True

        return False
Exemple #2
0
class EditSymmetryPopup(BasePopup):

    def __init__(self, parent, project):

        self.parent         = parent
        self.ccpnProject    = project
        self.hProject        = project.currentHaddockProject
        self.molPartner     = None
        self.molecules      = []
        self.symmetrySet    = None
        self.symmetryOp     = None
        self.symmetryCode    = None
        self.waiting        = False

        BasePopup.__init__(self, parent=parent, title='Symmetry Operations')

        self.font = 'Helvetica 12'
        self.setFont()

    def body(self, guiFrame):

        guiFrame.grid_columnconfigure(0, weight=1)
        guiFrame.grid_rowconfigure(1, weight=1)

        frame = LabelFrame(guiFrame, text='Options')
        frame.grid(row=0,column=0,sticky='ew')
        frame.grid_columnconfigure(5, weight=1)

        # Choose type of symmetry set to define or change (if allready present in the model)
        self.molLabel = Label(frame, text='Symmetry Operator:')
        self.molLabel.grid(row=0,column=2,sticky='w')
        self.symmCodePulldown = PulldownMenu(frame, callback=self.setSymmCode, entries=['NCS','C2','C3','C5'], do_initial_callback=False)
        self.symmCodePulldown.grid(row=0,column=3,sticky='w')

        frame = LabelFrame(guiFrame, text='Symmetry Operations')
        frame.grid(row=1,column=0,sticky='nsew')
        frame.grid_columnconfigure(0, weight=1)
        frame.grid_rowconfigure(0, weight=1)

        self.molSysPulldown   = PulldownMenu(self, callback=self.setMolSystem, do_initial_callback=False)
        self.chainSelect      = MultiWidget(self, CheckButton, callback=self.setChains, minRows=0, useImages=False)
        self.segStartEntry    = IntEntry(self, returnCallback=self.setSegStart, width=6)
        self.segLengthEntry   = IntEntry(self, returnCallback=self.setSegLength, width=6)

        headings = ['#','Symmetry\noperator','Mol System','Chains','Start\nresidue','Segment\nlength']  
        editWidgets      = [None, None, self.molSysPulldown, self.chainSelect, self.segStartEntry, self.segLengthEntry] 
        editGetCallbacks = [None, None, self.getMolSystem, self.getChains, self.getSegStart, self.getSegLength]
        editSetCallbacks = [None, self.setSymmCode, self.setMolSystem, self.setChains, self.setSegStart, self.setSegLength]
        
        self.symmetryMatrix = ScrolledMatrix(frame,headingList=headings,
                                             callback=self.selectSymmetry,
                                             editWidgets=editWidgets,
                                             editGetCallbacks=editGetCallbacks,
                                             editSetCallbacks=editSetCallbacks)
        self.symmetryMatrix.grid(row=0,column=0,sticky='nsew')

        texts = ['Add Symmetry Set','Remove Symmetry Set']
        commands = [self.addSymmetrySet,self.removeSymmetrySet]
        self.buttonList = createDismissHelpButtonList(guiFrame, texts=texts, commands=commands, expands=True)
        self.buttonList.grid(row=2,column=0,sticky='ew')
        
        self.updateMolPartners()
        self.notify(self.registerNotify)

        #Temporary report of parameters
        print self.molSystem
        print self.molecules
        print self.symmetrySet
        print self.symmetryOp
        print self.symmetryCode

    def getMolSystem(self, partner):

        """Select molecular system from list of molsystems stored in the project"""

        names = []; index = -1
        molSystem = partner.molSystem
        molSystems = self.ccpnProject.sortedMolSystems()
        if molSystems:
            names = [ms.code for ms in molSystems]
            if molSystem not in molSystems: 
                molSystem = molSystems[0]
                index = molSystems.index(molSystem)

        self.molSysPulldown.setup(names, index)

    def getChains(self, partner):
  
        names  = []
        values = []

        molSystem = partner.molSystem
        if molSystem:
            for chain in molSystem.sortedChains():
                names.append(chain.code)

                if not partner.chains:
                  values.append(True)

                elif partner.findFirstChain(chain=chain):
                  values.append(True)

                else:
                  values.append(False)   

                self.chainSelect.set(values=values,options=names)   

        else:
            showWarning('Warning','Set Mol System or ensemble first',parent=self)
            self.symmetryMatrix.keyPressEscape()        
    
    def getSegLength(self, symmetryOp):
        
        if symmetryOp and symmetryOp.segmentLength: self.segLengthEntry.set(symmetryOp.segmentLength)

    def getSegStart(self):
    
        pass

    def setMolSystem(self, partner, name=None):
  
        """Get all molsystems as stored in the project as list"""

        index =  self.molSysPulldown.getSelectedIndex()
        molSystems = self.ccpnProject.sortedMolSystems()
  
        if molSystems:
            molSystem = molSystems[index]
            self.molPartner.molSystem = molSystem
            chains = molSystem.sortedChains()
            if (not self.molPartner.chains) and chains: setPartnerChains(self.molPartner,chains)

        self.updateAllAfter()

    def setChains(self, obj):

        """Get the list of chains for the selected molsystem"""

        if self.molPartner and self.molPartner.molSystem:
            if obj is not None:
                chains = self.molPartner.molSystem.sortedChains()
                values = self.chainSelect.get()
                chains = [chains[i] for i in range(len(values)) if values[i]]
                setPartnerChains(self.molPartner,chains)

        self.symmetryMatrix.keyPressEscape()  
        self.updateAllAfter()
        
    def setSegLength(self, event):
        
        value = self.segLengthEntry.get() or 1
        self.symmetryOp.segmentLength = value

    def setSegStart(self):
        
        pass    

    def setSymmCode(self, index, name=None): 

        self.symmetryCode = self.symmCodePulldown.getSelected()    

    def selectSymmetry(self, obj, row, col):
        
        self.symmetryOp = obj
        self.updateMolSysPulldown()

    def notify(self, notifyFunc):
  
        for func in ('__init__', 'delete', 'setSymmetryCode','setSegmentLength'):
            notifyFunc(self.updateAllAfter, 'molsim.Symmetry.Symmetry', func)

        for func in ('__init__', 'delete','setfirstSeqId'):
            notifyFunc(self.updateAllAfter, 'molsim.Symmetry.Segment', func)

    def addSymmetrySet(self):
            
        if not self.ccpnProject.molSystems:
            showWarning('Warning','No molecular systems present in CCPN project',parent=self)
            return

            molSystem = self.ccpnProject.findFirstMolSystem()
            partner = self.hProject.newHaddockPartner(code=molSystem.code, molSystem=molSystem)
            setPartnerChains(partner, molSystem.chains)

            self.updateAllAfter()
            
    def removeSymmetrySet(self):
        
        pass            
    
    def updateAllAfter(self, obj=None):

        if self.waiting: return
        else:
            self.waiting = True
            self.after_idle(self.updateSymmetries,
                            self.updateMolPartners)
    
    def updateMolPartners(self):

        textMatrix = []
        objectList = []

        for partner in self.hProject.sortedHaddockPartners():
            datum = [partner.code,
                     self.symmetryCode,
                     partner.molSystem.code,
                     ','.join([c.chain.code for c in partner.chains]),
                     partner.autoHistidinePstate and 'Yes' or 'No',
                     partner.isDna and 'Yes' or 'No']
            
            objectList.append(partner)
            textMatrix.append(datum)

        self.symmetryMatrix.update(objectList=objectList, textMatrix=textMatrix)
        self.updateMolSysPulldown()
    
    def updateMolSysPulldown(self):

        names = []
        index = -1

        partners = self.hProject.sortedHaddockPartners()

        if partners:
            if self.molPartner not in partners:
                self.molPartner = partners[0]

            names = ['Partner %s' % p.code for p in partners]
            index = partners.index(self.molPartner)

        else: self.molPartner = None

        self.molSysPulldown.setup(names, index)
        
    def updateSymmetries(self):

        textMatrix  = []; objectList  = []
        if self.symmetrySet:
            for symmetryOp in self.symmetrySet.symmetries:
                chains = []; segments = [] 
                length = symmetryOp.segmentLength

                for segment in symmetryOp.sortedSegments():
                    code = segment.chainCode
                    chain = self.molSystem.findFirstChain(code=code)

                    if chain:
                        chains.append(code)
                        seqId = segment.firstSeqId
                        residue1 = chain.findFirstResidue(seqId=seqId)
                        residue2 = chain.findFirstResidue(seqId=seqId+length-1)
                        segments.append('%s:%d-%d' % (code,residue1.seqCode,residue2.seqCode))

                datum = [symmetryOp.serial, symmetryOp.symmetryCode, length, '\n'.join(chains), '\n'.join(segments)]
                objectList.append(symmetryOp)
                textMatrix.append(datum)

        self.symmetryMatrix.update(objectList=objectList, textMatrix=textMatrix)
        self.waiting = False

    def destroy(self):

        self.notify(self.unregisterNotify)
        BasePopup.destroy(self)
Exemple #3
0
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)
Exemple #4
0
class MidgePopup(BasePopup):
    def __init__(self, parent, *args, **kw):

        self.guiParent = parent
        self.project = parent.getProject()
        self.waiting = 0
        self.specFreq = 800.13
        self.maxIter = 15
        self.mixTime = 60
        self.corrTime = 11.5
        self.leakRate = 2.0
        self.ratioHD = 0.9
        self.peakListDict = {}
        self.peakListDict3d = {}
        self.noesyPeakList = None
        self.noesy3dPeakList = None
        self.carbonLabel = 0
        self.nitrogenLabel = 1
        self.noesyPeakList1 = None
        self.noesyPeakList2 = None
        self.noesyPeakList3 = None
        self.noesyPeakList3d = None

        self.resonances = None
        self.noesyPeaks = None
        self.distanceConstraintList = None
        self.antiDistConstraintList = None
        self.adcAtomTypes = None
        self.structure = None

        BasePopup.__init__(self,
                           parent,
                           title="Relaxation Matrix Optimisation",
                           **kw)

    def body(self, guiFrame):

        self.specFreqEntry = IntEntry(self,
                                      text=self.specFreq,
                                      width=8,
                                      returnCallback=self.setSpecFreq)
        self.maxIterEntry = IntEntry(self,
                                     text=self.maxIter,
                                     width=8,
                                     returnCallback=self.setMaxIter)
        self.mixTimeEntry = FloatEntry(self,
                                       text=self.mixTime,
                                       width=8,
                                       returnCallback=self.setMixTime)
        self.corrTimeEntry = FloatEntry(self,
                                        text=self.corrTime,
                                        width=8,
                                        returnCallback=self.setCorrTime)
        self.leakRateEntry = FloatEntry(self,
                                        text=self.leakRate,
                                        width=8,
                                        returnCallback=self.setLeakRate)

        guiFrame.grid_columnconfigure(0, weight=1)
        guiFrame.grid_rowconfigure(1, weight=1)

        row = 0
        labelFrame0 = LabelFrame(guiFrame, text='Input data')
        labelFrame0.grid(row=row, column=0, sticky=Tkinter.NSEW)
        labelFrame0.grid_columnconfigure(3, weight=1)

        label = Label(labelFrame0, text='Assigned NOESY spectrum')
        label.grid(row=0, column=0, sticky=Tkinter.NW)
        self.noesyPulldown = PulldownMenu(labelFrame0,
                                          entries=self.getNoesys(),
                                          callback=self.setNoesy,
                                          selected_index=0,
                                          do_initial_callback=0)
        self.noesyPulldown.grid(row=0, column=1, sticky=Tkinter.NW)

        label = Label(labelFrame0, text='H/D ratio: ')
        label.grid(row=0, column=2, sticky=Tkinter.NW)
        self.ratioHDEntry = FloatEntry(labelFrame0, text=self.ratioHD, width=6)
        self.ratioHDEntry.grid(row=0, column=3, sticky=Tkinter.NW)

        label = Label(labelFrame0, text='NOESY spectrum 1:')
        label.grid(row=1, column=0, sticky=Tkinter.NW)
        self.tmix1Pulldown = PulldownMenu(labelFrame0,
                                          entries=self.getNoesys(),
                                          callback=self.setNoesy1,
                                          selected_index=-0,
                                          do_initial_callback=0)
        self.tmix1Pulldown.grid(row=1, column=1, sticky=Tkinter.NW)
        label = Label(labelFrame0, text='Tmix (ms): ')
        label.grid(row=1, column=2, sticky=Tkinter.NW)
        self.tmix1Entry = FloatEntry(labelFrame0, text=60, width=6)
        self.tmix1Entry.grid(row=1, column=3, sticky=Tkinter.NW)

        label = Label(labelFrame0, text='NOESY spectrum 2:')
        label.grid(row=2, column=0, sticky=Tkinter.NW)
        self.tmix2Pulldown = PulldownMenu(labelFrame0,
                                          entries=self.getNoesys(),
                                          callback=self.setNoesy2,
                                          selected_index=0,
                                          do_initial_callback=0)
        self.tmix2Pulldown.grid(row=2, column=1, sticky=Tkinter.NW)
        label = Label(labelFrame0, text='Tmix (ms): ')
        label.grid(row=2, column=2, sticky=Tkinter.NW)
        self.tmix2Entry = FloatEntry(labelFrame0, text=120, width=6)
        self.tmix2Entry.grid(row=2, column=3, sticky=Tkinter.NW)

        label = Label(labelFrame0, text='NOESY spectrum 3:')
        label.grid(row=3, column=0, sticky=Tkinter.NW)
        self.tmix3Pulldown = PulldownMenu(labelFrame0,
                                          entries=self.getNoesys(),
                                          callback=self.setNoesy3,
                                          selected_index=0,
                                          do_initial_callback=0)
        self.tmix3Pulldown.grid(row=3, column=1, sticky=Tkinter.NW)
        label = Label(labelFrame0, text='Tmix (ms): ')
        label.grid(row=3, column=2, sticky=Tkinter.NW)
        self.tmix3Entry = FloatEntry(labelFrame0, text=200, width=6)
        self.tmix3Entry.grid(row=3, column=3, sticky=Tkinter.NW)

        label = Label(labelFrame0, text='3D NOESY:')
        label.grid(row=4, column=0, sticky=Tkinter.NW)
        self.noesy3dPulldown = PulldownMenu(labelFrame0,
                                            entries=self.getNoesys3d(),
                                            callback=self.setNoesy3d,
                                            selected_index=0,
                                            do_initial_callback=0)
        self.noesy3dPulldown.grid(row=4, column=1, sticky=Tkinter.NW)

        label10 = Label(labelFrame0, text='Num peaks:')
        label10.grid(row=5, column=0, sticky=Tkinter.NW)
        self.numPeaksLabel = Label(labelFrame0, text='0')
        self.numPeaksLabel.grid(row=5, column=1, sticky=Tkinter.NW)

        label11 = Label(labelFrame0, text='Num resonances:')
        label11.grid(row=5, column=2, sticky=Tkinter.NW)
        self.numResonancesLabel = Label(labelFrame0, text='0')
        self.numResonancesLabel.grid(row=5, column=3, sticky=Tkinter.NW)

        row += 1
        labelFrame1 = LabelFrame(guiFrame, text='Parameters')
        labelFrame1.grid(row=row, column=0, sticky=Tkinter.NSEW)
        labelFrame1.grid_columnconfigure(3, weight=1)

        label = Label(labelFrame1, text='15N labelled sample:')
        label.grid(row=0, column=0, sticky=Tkinter.NW)
        self.nitrogenSelect = CheckButton(labelFrame1,
                                          callback=self.setNitrogenLabel)
        self.nitrogenSelect.grid(row=0, column=1, sticky=Tkinter.W)
        self.nitrogenSelect.set(1)

        label = Label(labelFrame1, text='13C labelled sample:')
        label.grid(row=0, column=2, sticky=Tkinter.NW)
        self.carbonSelect = CheckButton(labelFrame1,
                                        callback=self.setCarbonLabel)
        self.carbonSelect.grid(row=0, column=3, sticky=Tkinter.W)
        self.carbonSelect.set(0)

        labelFrame1.grid_rowconfigure(1, weight=1)
        data = [
            self.specFreq, self.maxIter, self.mixTime, self.corrTime,
            self.leakRate
        ]
        colHeadings = [
            'Spectrometer\nfrequency', 'Max\niterations', 'Mixing\ntime (ms)',
            'Correl.\ntime (ns)', 'Leak\nrate'
        ]
        editWidgets = [
            self.specFreqEntry,
            self.maxIterEntry,
            self.mixTimeEntry,
            self.corrTimeEntry,
            self.leakRateEntry,
        ]
        editGetCallbacks = [
            self.getSpecFreq,
            self.getMaxIter,
            self.getMixTime,
            self.getCorrTime,
            self.getLeakRate,
        ]
        editSetCallbacks = [
            self.setSpecFreq,
            self.setMaxIter,
            self.setMixTime,
            self.setCorrTime,
            self.setLeakRate,
        ]
        self.midgeParamsMatrix = ScrolledMatrix(
            labelFrame1,
            editSetCallbacks=editSetCallbacks,
            editGetCallbacks=editGetCallbacks,
            editWidgets=editWidgets,
            maxRows=1,
            initialCols=5,
            headingList=colHeadings,
            callback=None,
            objectList=[
                'None',
            ],
            textMatrix=[
                data,
            ])
        self.midgeParamsMatrix.grid(row=1,
                                    column=0,
                                    columnspan=4,
                                    sticky=Tkinter.NSEW)

        label10 = Label(labelFrame1, text='Benchmark structure')
        label10.grid(row=2, column=0, sticky=Tkinter.NW)
        self.structurePulldown = PulldownMenu(labelFrame1,
                                              entries=self.getStructures(),
                                              callback=self.setStructure,
                                              selected_index=0,
                                              do_initial_callback=0)
        self.structurePulldown.grid(row=2, column=1, sticky=Tkinter.NW)

        label11 = Label(labelFrame1, text='ADC atom types:')
        label11.grid(row=2, column=2, sticky=Tkinter.NW)
        self.adcAtomsPulldown = PulldownMenu(labelFrame1,
                                             entries=self.getAdcAtomTypes(),
                                             callback=self.setAdcAtomTypes,
                                             selected_index=0,
                                             do_initial_callback=0)
        self.adcAtomsPulldown.grid(row=2, column=3, sticky=Tkinter.NW)

        row += 1
        labelFrame2 = LabelFrame(guiFrame, text='Output')
        labelFrame2.grid(row=row, column=0, sticky=Tkinter.NSEW)
        labelFrame2.grid_columnconfigure(3, weight=1)

        label20 = Label(labelFrame2, text='Distance constraints:')
        label20.grid(row=0, column=0, sticky=Tkinter.NW)
        self.distConstrLabel = Label(labelFrame2, text='0')
        self.distConstrLabel.grid(row=0, column=1, sticky=Tkinter.NW)

        label21 = Label(labelFrame2, text='Anti-distance constraints:')
        label21.grid(row=0, column=2, sticky=Tkinter.NW)
        self.antiConstrLabel = Label(labelFrame2, text='0')
        self.antiConstrLabel.grid(row=0, column=3, sticky=Tkinter.NW)

        texts = [
            'Calculate distances', 'Show distance\nconstraints',
            'Show anti-distance\nconstraints'
        ]
        commands = [
            self.calculateDistances, self.showConstraints,
            self.showAntiConstraints
        ]
        self.midgeButtons = ButtonList(labelFrame2,
                                       expands=1,
                                       texts=texts,
                                       commands=commands)
        self.midgeButtons.grid(row=1,
                               column=0,
                               columnspan=4,
                               sticky=Tkinter.NSEW)

        row += 1
        self.bottomButtons = createDismissHelpButtonList(guiFrame,
                                                         expands=0,
                                                         help_url=None)
        self.bottomButtons.grid(row=row,
                                column=0,
                                columnspan=4,
                                sticky=Tkinter.EW)

        self.getPeaks()
        self.getResonances()
        self.update()

        self.geometry('600x400')

    def setCarbonLabel(self, boolean):

        self.carbonLabel = boolean

    def setNitrogenLabel(self, boolean):

        self.nitrogenLabel = boolean

    def update(self):

        if self.resonances and (
            (self.noesyPeaks and self.noesyPeakList1 and self.noesyPeakList2
             and self.noesyPeakList3) or self.noesyPeakList3d):
            self.midgeButtons.buttons[0].enable()
        else:
            self.midgeButtons.buttons[0].disable()

        if self.distanceConstraintList:
            self.distConstrLabel.set(
                str(len(self.distanceConstraintList.constraints)))
            self.midgeButtons.buttons[1].enable()
        else:
            self.distConstrLabel.set('')
            self.midgeButtons.buttons[1].disable()

        if self.antiDistConstraintList:
            self.antiConstrLabel.set(
                str(len(self.antiDistConstraintList.constraints)))
            self.midgeButtons.buttons[2].enable()
        else:
            self.antiConstrLabel.set('')
            self.midgeButtons.buttons[2].disable()

        if self.resonances:
            self.numResonancesLabel.set(str(len(self.resonances)))
        else:
            self.numResonancesLabel.set('')

        if self.noesyPeaks:
            self.numPeaksLabel.set(str(len(self.noesyPeaks)))
        else:
            self.numPeaksLabel.set('')

    def getStructures(self):

        names = [
            '<None>',
        ]
        for molSystem in self.project.sortedMolSystems():
            for structure in molSystem.sortedStructureEnsembles():
                names.append('%s:%d' % (molSystem.name, structure.ensembleId))

        return names

    def setStructure(self, index, name=None):

        if index < 1:
            self.structure = None
        else:
            structures = []
            for molSystem in self.project.molSystems:
                for structure in molSystem.structureEnsembles:
                    structures.append(structure)

            self.structure = structures[index - 1]

    def getAdcAtomTypes(self):

        return ['<None>', 'HN', 'HN HA', 'HN HA HB']

    def setAdcAtomTypes(self, index, name=None):

        if name is None:
            name = self.adcAtomsPulldown.getSelected()

        if name == '<None>':
            name = None

        self.adcAtomTypes = name

    def getResonances(self):

        resonanceDict = {}
        if self.noesyPeaks:
            for peak in self.noesyPeaks:
                for peakDim in peak.peakDims:
                    for contrib in peakDim.peakDimContribs:
                        resonanceDict[contrib.resonance] = 1
                        # TBD: Set resonance.name for typing

        self.resonances = resonanceDict.keys()

    def getPeaks(self):

        if self.noesyPeakList:
            self.noesyPeaks = self.noesyPeakList.sortedPeaks()

    def calculateDistances(self):

        resonances = list(self.resonances)

        resDict = {}
        for resonance in resonances:
            resDict[resonance.serial] = resonance

        ratioHD = self.ratioHDEntry.get() or self.ratioHD

        tmix1 = self.tmix1Entry.get() or 60
        tmix2 = self.tmix2Entry.get() or 120
        tmix3 = self.tmix3Entry.get() or 200

        data = [(tmix1, self.noesyPeakList1), (tmix2, self.noesyPeakList2),
                (tmix3, self.noesyPeakList3)]
        data.sort()

        mixingTimes = [x[0] for x in data]
        peakLists = [x[1] for x in data]

        # get a clean, symmetric and normalised NOE matrix
        noeMatrix = getNoeMatrixFromPeaks(self.noesyPeaks,
                                          resonances,
                                          peakLists,
                                          mixingTimes,
                                          ratioHD=ratioHD,
                                          analysis=self.guiParent)

        # optimiseRelaxation will remove unconstrained resonances
        self.distanceConstraintList, resonances = optimiseRelaxation(
            resonances,
            noeMatrix,
            self.mixTime,
            self.specFreq,
            self.corrTime,
            self.leakRate,
            self.carbonLabel,
            self.nitrogenLabel,
            maxIter=self.maxIter)

        #constrainSpinSystems(self.distanceConstraintList)
        # for testing calculate distances from structure overrides any resonances: uses assigned ones
        #(self.distanceConstraintList, self.resonances) = self.cheatForTesting()
        #self.antiDistConstraintList = self.distanceConstraintList
        protonNumbs = {'CH3': 3, 'Haro': 2, 'HN': 1, 'H': 1}

        PI = 3.1415926535897931
        GH = 2.6752e4
        HB = 1.05459e-27
        CONST = GH * GH * GH * GH * HB * HB
        tc = 1.0e-9 * self.corrTime
        wh = 2.0 * PI * self.specFreq * 1.0e6
        j0 = CONST * tc
        j1 = CONST * tc / (1.0 + wh * wh * tc * tc)
        j2 = CONST * tc / (1.0 + 4.0 * wh * wh * tc * tc)
        #jself = 6.0*j2 + 3.0*j1 + j0
        jcross = 6.0 * j2 - j0

        if self.distanceConstraintList and self.noesyPeakList:
            constraintHead = self.distanceConstraintList.nmrConstraintStore

            if self.adcAtomTypes:
                adcDict = {
                    'HN': ['H'],
                    'HN HA': ['H', 'HA', 'HA1', 'HA2'],
                    'HN HA HB': ['H', 'HA', 'HA1', 'HA2', 'HB', 'HB2', 'HB3']
                }

                allowedAtomTypes = adcDict[self.adcAtomTypes]

                print "Making ADCs"
                self.antiDistConstraintList = makeNoeAdcs(
                    resonances[:],
                    self.noesyPeakList.dataSource,
                    constraintHead,
                    allowedAtomTypes=allowedAtomTypes)
                print "Done ADCs"

            if self.structure:

                N = len(self.resonances)
                sigmas = [[] for i in range(N)]
                for i in range(N):
                    sigmas[i] = [0.0 for j in range(N)]

                for constraint in self.distanceConstraintList.constraints:
                    item = constraint.findFirstItem()
                    resonances = list(item.resonances)

                    ri = resDict[resonances[0].resonanceSerial]
                    rj = resDict[resonances[1].resonanceSerial]
                    i = self.resonances.index(ri)
                    j = self.resonances.index(rj)
                    atomSets1 = list(ri.resonanceSet.atomSets)
                    atomSets2 = list(rj.resonanceSet.atomSets)
                    if atomSets1 == atomSets2:
                        ass = list(atomSets1)
                        atomSets1 = [
                            ass[0],
                        ]
                        atomSets2 = [
                            ass[-1],
                        ]

                    distance = getAtomSetsDistance(atomSets1, atomSets2,
                                                   self.structure)
                    r = distance * 1e-8
                    nhs = protonNumbs[rj.name]
                    sigma = 0.1 * jcross * nhs / (r**6)
                    sigmas[i][j] = sigma

                    constraint.setOrigData(distance)

        self.update()

    def showConstraints(self):

        if self.distanceConstraintList:
            self.guiParent.browseConstraints(
                constraintList=self.distanceConstraintList)

    def showAntiConstraints(self):

        if self.antiDistConstraintList:
            self.guiParent.browseConstraints(
                constraintList=self.antiDistConstraintList)

    def getNoesys3d(self):

        peakLists = getThroughSpacePeakLists(self.project)

        names = [
            '<None>',
        ]
        for peakList in peakLists:
            spectrum = peakList.dataSource
            if spectrum.numDim != 3:
                continue

            name = '%s:%s:%s' % (spectrum.experiment.name, spectrum.name,
                                 peakList.serial)
            names.append(name)
            self.peakListDict3d[name] = peakList
            if not self.noesyPeakList:
                self.noesyPeakList = peakList

        return names

    def getNoesys(self):

        peakLists = getThroughSpacePeakLists(self.project)

        names = [
            '<None>',
        ]
        for peakList in peakLists:
            spectrum = peakList.dataSource
            name = '%s:%s:%s' % (spectrum.experiment.name, spectrum.name,
                                 peakList.serial)
            names.append(name)
            self.peakListDict[name] = peakList

            if not self.noesyPeakList:
                self.noesyPeakList = peakList

        return names

    def setNoesy(self, index, name=None):

        if not name:
            name = self.noesyPulldown.getSelected()

        if name == '<None>':
            self.noesyPeakList = None

        else:
            self.noesyPeakList = self.peakListDict[name]

        self.getPeaks()
        self.getResonances()
        self.update()

    def setNoesy1(self, index, name=None):

        if not name:
            name = self.tmix1Pulldown.getSelected()

        if name != '<None>':
            self.noesyPeakList1 = self.peakListDict[name]
        else:
            self.noesyPeakList1 = None

        self.update()

    def setNoesy2(self, index, name=None):

        if not name:
            name = self.tmix2Pulldown.getSelected()

        if name != '<None>':
            self.noesyPeakList2 = self.peakListDict[name]
        else:
            self.noesyPeakList2 = None

        self.update()

    def setNoesy3(self, index, name=None):

        if not name:
            name = self.tmix3Pulldown.getSelected()

        if name != '<None>':
            self.noesyPeakList3 = self.peakListDict[name]
        else:
            self.noesyPeakList3 = None

        self.update()

    def setNoesy3d(self, index, name=None):

        if not name:
            name = self.noesy3dPulldown.getSelected()

        if name != '<None>':
            self.noesyPeakList3d = self.peakListDict3d[name]
            self.noesyPeaks = self.noesyPeakList3d.sortedPeaks()

        else:
            self.noesyPeakList3d = None
            self.noesyPeaks = []

        self.getResonances()
        self.update()

    def updateMidgeParams(self):

        data = [
            self.specFreq, self.maxIter, self.mixTime, self.corrTime,
            self.leakRate
        ]

        self.midgeParamsMatrix.update(textMatrix=[
            data,
        ])

    def getSpecFreq(self, obj):

        self.specFreqEntry.set(self.specFreq)

    def getMaxIter(self, obj):

        self.maxIterEntry.set(self.maxIter)

    def getMixTime(self, obj):

        self.mixTimeEntry.set(self.mixTime)

    def getCorrTime(self, obj):

        self.corrTimeEntry.set(self.corrTime)

    def getLeakRate(self, obj):

        self.leakRateEntry.set(self.leakRate)

    def setSpecFreq(self, event):

        value = self.specFreqEntry.get()
        if value is not None:
            self.specFreq = value

        self.updateMidgeParams()

    def setMaxIter(self, event):

        value = self.maxIterEntry.get()
        if value is not None:
            self.maxIter = value

        self.updateMidgeParams()

    def setMixTime(self, event):

        value = self.mixTimeEntry.get()
        if value is not None:
            self.mixTime = value

        self.updateMidgeParams()

    def setCorrTime(self, event):

        value = self.corrTimeEntry.get()
        if value is not None:
            self.corrTime = value

        self.updateMidgeParams()

    def setLeakRate(self, event):

        value = self.leakRateEntry.get()
        if value is not None:
            self.leakRate = value

        self.updateMidgeParams()

    def destroy(self):

        BasePopup.destroy(self)
Exemple #5
0
class EditPeakAliasingPopup(BasePopup):
    """
  **Move Aliased Peaks to Their Underlying Resonance Position**
  
  This popup window is used to move aliased 'ghost' peaks to their real
  underlying resonance locations by adding or subtracting a whole number of
  spectrum widths to the position in one or more peak dimensions. This is used
  when a resonance, that causes a peak, lies outside the normal recorded bounds
  of the spectrum but the peak nonetheless still appears within the spectrum, as
  an aliased signal that re-appears as if wrapped back onto the opposite side of
  the spectrum.

  The minimum and maximum aliased frequency values for a spectrum dimension, as
  edited in the "Referencing" table of the main Spectra_ popup, may be set
  extend the contour display beyond the normal sweep width (ppm range) of the
  spectrum and thus cover the real ppm position of peaks that have been
  unaliased. In such instances the contours are extended by tiling; one or more
  copies of the contours (not mirror image) are made and placed sequentially
  next to the normal, fundamental region. If a peak is unaliased to a position
  outside the displayed spectrum limits then the contour display will naturally
  be extended to cover the new peak position; all peaks will be visible within
  the spectrum by default. However, the user may at any time reset the minimum
  and maximum aliased frequency for a spectrum display (see the Spectra_ popup);
  deleting all values will reset bounds to the original sweep with, but any
  values may be chosen, within reason.

  A peak can be moved to the unaliased position of its underlying resonances by
  editing the "Num. Aliasing" column of this popup; double-clicking and typing in
  the appropriate number. When this number is changed for a peak dimension the peak
  will be instantly moved to its new location. An aliasing value of zero means that
  the peak lies within the sweep width of the spectrum. For a ppm scale having a
  positive aliasing value will *reduce* the ppm value, placing the peak a number of
  spectrum widths above or to the right of the spectrum bounds. Likewise a negative
  aliasing value will place a peak at a higher ppm value; below or to the left.
  Often aliasing values will be 1 or -1, where a peak has just fallen off the edge
  of a spectrum. For example a glycine amide nitrogen really at 100 ppm may be just
  outside the top of a 15N HSQC and be wrapped back into the bottom to appear as a
  peak at around 135 ppm, which means that the aliasing value should be set to 1,
  moving the peaks position up by a sweep with of 35 ppm, from 135 ppm to 100 ppm.
  The sign of the aliasing number may seem to be backwards, but it is perhaps the
  ppm scale that is 'backwards'. More complex aliasing is often seen in 3D 13C
  spectra where the 13C axis can be folded (wrapped) to reduce the sweep width that
  needs to be recorded, but still avoiding peak overlap because of the way shifts
  correlate. For example a 13C HSQC NOESY may be recorded with a sweep with that
  covers the CA, CB range 25-75 ppm but aliased methyl carbons below 25 ppm and
  aromatic carbons between 110 ppm and 140 ppm will be present; the methyls will
  have aliasing values of 1, and the aromatics -1 or -2.

  It should be noted that picking peaks in the tiled copies of a contour display,
  i.e. outside the sweep width, will automatically set the aliasing value for the
  peak to reflect the displayed chemical shift value. Thus, the user does not need
  to explicitly unalias the peak position. Any peaks that are moved by virtue of
  being unaliased will have their contribution to the chemical shifts, of any
  assigned resonances, adjusted automatically. Chemical shift values are always
  calculated using the underlying resonance positions, not the apparent peak
  position. Also, if it is known that many peaks share the same aliasing values,
  i.e. are in the same sweep width tile, then the user can propagate the aliasing
  value from one peak to many others in a single step via the right-click window
  menu; "Peak::Unaliasing propagate".

  .. _Spectra: EditSpectrumPopup.html
  
  """
    def __init__(self, parent, peak=None, *args, **kw):

        self.peak = peak
        self.peakDim = None
        self.guiParent = parent

        BasePopup.__init__(self,
                           parent=parent,
                           title="Edit Peak Aliasing",
                           **kw)

    def body(self, guiFrame):

        self.geometry("500x250")

        self.numAliasingEntry = IntEntry(self,
                                         text='',
                                         returnCallback=self.setNumAliasing,
                                         width=4)

        guiFrame.expandGrid(1, 0)

        div = LabelDivider(guiFrame,
                           text='Peak Dimension Positions',
                           grid=(0, 0))
        utilButtons = UtilityButtonList(guiFrame,
                                        doClone=False,
                                        closeCmd=self.close,
                                        helpUrl=self.help_url,
                                        grid=(0, 1))

        tipTexts = [
            'The peak/spectrum dimension number',
            'The kind of isotope measured in the dimension',
            'The position of the peak in this dimension, in units of ppm',
            'The frequency position of the peak in this dimension, in units of Hz',
            'The data point position (in the spectrum matrix) of the peak in this dimension',
            'Sets the number of spectrum sweep withs to add to the peak dimension position to locate it at its real ppm value. Note an aliasing of "1" moves a peak to a lower ppm',
            'The assignment annotation for the peak dimension'
        ]

        headingList = [
            'Dimension', 'Isotope', 'ppm', 'Hz', 'Points', 'Num.\nAliasing',
            'Annotation'
        ]
        editWidgets = [
            None, None, None, None, None, self.numAliasingEntry, None
        ]
        editGetCallbacks = [
            None, None, None, None, None, self.getNumAliasing, None
        ]
        editSetCallbacks = [
            None, None, None, None, None, self.setNumAliasing, None
        ]
        self.scrolledMatrix = ScrolledMatrix(guiFrame,
                                             tipTexts=tipTexts,
                                             editSetCallbacks=editSetCallbacks,
                                             editGetCallbacks=editGetCallbacks,
                                             editWidgets=editWidgets,
                                             initialCols=5,
                                             initialRows=3,
                                             headingList=headingList,
                                             callback=self.selectCell,
                                             grid=(1, 0),
                                             gridSpan=(1, 2))

        for func in ('__init__', 'delete', 'setAnnotation', 'setNumAliasing',
                     'setPosition'):
            self.registerNotify(self.updateAfter, 'ccp.nmr.Nmr.PeakDim', func)

        self.waiting = False
        self.updateAfter(self.peak)

    def open(self):

        self.updateAfter()
        BasePopup.open(self)

    def setNumAliasing(self, event):

        value = self.numAliasingEntry.get()
        if (value is not None) and self.peakDim:
            setPeakDimNumAliasing(self.peakDim, value)
            self.updateAfter()

    def getNumAliasing(self, peakDim):

        if peakDim:
            self.numAliasingEntry.set(peakDim.numAliasing)

    def selectCell(self, object, row, col):

        self.peakDim = object

    def updateAfter(self, object=None):

        if object:
            if object.className == 'Peak':
                self.peak = object
            elif object.peak is not self.peak:
                # object is peakDim & function was called by notifier
                # return if not my peak
                return

        if self.waiting:
            return
        else:
            self.waiting = True
            self.after_idle(self.update)

    def update(self):

        objectList = []
        textMatrix = []
        colorMatrix = []
        colors = [None] * 7
        colors[5] = '#B0FFB0'

        if self.peak:
            for peakDim in self.peak.sortedPeakDims():
                dataDimRef = peakDim.dataDimRef
                if dataDimRef:
                    objectList.append(peakDim)
        else:
            textMatrix.append([])

        for peakDim in objectList:
            dataDimRef = peakDim.dataDimRef
            expDimRef = dataDimRef.expDimRef
            position = aliasedPeakDimPosition(peakDim)
            datum = [
                peakDim.dim, '/'.join(expDimRef.isotopeCodes),
                unit_converter[('point', 'ppm')](position, dataDimRef),
                unit_converter[('point', 'Hz')](position, dataDimRef),
                position, peakDim.numAliasing, peakDim.annotation
            ]

            textMatrix.append(datum)
            colorMatrix.append(colors)

        self.scrolledMatrix.update(objectList=objectList,
                                   textMatrix=textMatrix,
                                   colorMatrix=colorMatrix)
        self.waiting = False

    def destroy(self):

        for func in ('__init__', 'delete', 'setAnnotation', 'setNumAliasing',
                     'setPosition'):
            self.unregisterNotify(self.updateAfter, 'ccp.nmr.Nmr.PeakDim',
                                  func)

        BasePopup.destroy(self)
Exemple #6
0
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 EditSymmetryPopup(BasePopup):
    def __init__(self, parent, project):

        self.parent = parent
        self.project = project
        self.singleMolecule = True
        self.molSystem = None
        self.molecules = []
        self.symmetrySet = None
        self.symmetryOp = None
        self.waiting = False

        BasePopup.__init__(self, parent=parent, title='Symmetry Operations')

    def body(self, guiFrame):

        guiFrame.grid_columnconfigure(0, weight=1)
        guiFrame.grid_rowconfigure(1, weight=1)

        frame = LabelFrame(guiFrame, text='Options')
        frame.grid(row=0, column=0, sticky='ew')
        frame.grid_columnconfigure(5, weight=1)

        label = Label(frame, text='MolSystem:')
        label.grid(row=0, column=0, sticky='w')
        self.molSystemPulldown = PulldownMenu(frame,
                                              callback=self.selectMolSystem)
        self.molSystemPulldown.grid(row=0, column=1, sticky='w')

        self.molLabel = Label(frame, text='Molecule:')
        self.molLabel.grid(row=0, column=2, sticky='w')
        self.moleculePulldown = PulldownMenu(frame,
                                             callback=self.selectMolecule)
        self.moleculePulldown.grid(row=0, column=3, sticky='w')

        label = Label(frame, text='Same Molecule Symmetry:')
        label.grid(row=0, column=4, sticky='w')
        self.molSelect = CheckButton(frame, callback=self.toggleSingleMolecule)
        self.molSelect.grid(row=0, column=5, sticky='w')
        self.molSelect.set(self.singleMolecule)

        frame = LabelFrame(guiFrame, text='Symmetry Operations')
        frame.grid(row=1, column=0, sticky='nsew')
        frame.grid_columnconfigure(0, weight=1)
        frame.grid_rowconfigure(0, weight=1)

        self.symmCodePulldown = PulldownMenu(self,
                                             callback=self.setSymmCode,
                                             do_initial_callback=False)
        self.segLengthEntry = IntEntry(self,
                                       returnCallback=self.setSegLength,
                                       width=6)
        self.setChainMulti = MultiWidget(self,
                                         CheckButton,
                                         callback=self.setChains,
                                         minRows=0,
                                         useImages=False)
        self.setSegmentMulti = MultiWidget(self,
                                           IntEntry,
                                           callback=self.setSegments,
                                           minRows=0,
                                           useImages=False)

        editWidgets = [
            None, self.symmCodePulldown, self.segLengthEntry,
            self.setChainMulti, self.setSegmentMulti
        ]
        editGetCallbacks = [
            None, self.getSymmCode, self.getSegLength, self.getChains,
            self.getSegments
        ]
        editSetCallbacks = [
            None, self.setSymmCode, self.setSegLength, self.setChains,
            self.setSegments
        ]

        headings = [
            '#', 'Symmetry\nType', 'Segment\nLength', 'Chains',
            'Segment\nPositions'
        ]
        self.symmetryMatrix = ScrolledMatrix(frame,
                                             headingList=headings,
                                             callback=self.selectSymmetry,
                                             editWidgets=editWidgets,
                                             editGetCallbacks=editGetCallbacks,
                                             editSetCallbacks=editSetCallbacks)
        self.symmetryMatrix.grid(row=0, column=0, sticky='nsew')

        texts = ['Add Symmetry Op', 'Remove Symmetrey Op']
        commands = [self.addSymmOp, self.removeSymmOp]
        buttonList = createDismissHelpButtonList(guiFrame,
                                                 texts=texts,
                                                 commands=commands,
                                                 expands=True)
        buttonList.grid(row=2, column=0, sticky='ew')

        self.updateMolSystems()
        self.updateMolecules()
        self.updateSymmetriesAfter()

        self.notify(self.registerNotify)

    def open(self):

        self.updateMolSystems()
        self.updateMolecules()
        self.updateSymmetriesAfter()

        BasePopup.open(self)

    def notify(self, notifyFunc):

        for func in ('__init__', 'delete', 'setSymmetryCode',
                     'setSegmentLength'):
            notifyFunc(self.updateSymmetriesAfter, 'molsim.Symmetry.Symmetry',
                       func)

        for func in ('__init__', 'delete', 'setfirstSeqId'):
            notifyFunc(self.updateSymmetriesAfter, 'molsim.Symmetry.Segment',
                       func)

    def getSymmCode(self, symmetryOp):
        """Get allowed symmetry operators from the model"""

        symmetryOpCodes = symmetryOp.parent.metaclass.container.getElement(
            'SymmetryOpCode').enumeration
        index = 0

        if symmetryOp.symmetryCode in symmetryOpCodes:
            index = symmetryOpCodes.index(symmetryOp.symmetryCode)

        self.symmCodePulldown.setup(symmetryOpCodes, index)

    def getSegLength(self, symmetryOp):

        if symmetryOp and symmetryOp.segmentLength:
            self.segLengthEntry.set(symmetryOp.segmentLength)

    def getChains(self, symmetryOp):

        chains = []
        for chain in self.molSystem.chains:
            if chain.residues:
                if chain.molecule in self.molecules: chains.append(chain.code)
        chains.sort()

        values = []
        for chain in chains:
            if symmetryOp.findFirstSegment(chainCode=chain):
                values.append(True)
            else:
                values.append(False)

        self.setChainMulti.set(values=values, options=chains)

    def getSegments(self, symmetryOp):

        values = []
        names = []

        if symmetryOp:
            for segment in symmetryOp.sortedSegments():
                names.append(segment.chainCode)
                values.append(segment.firstSeqId)

        n = len(values)
        self.setSegmentMulti.maxRows = n
        self.setSegmentMulti.minRows = n
        self.setSegmentMulti.set(values=values, options=names)

    def setSymmCode(self, index, name=None):
        """Set the symmetry code as NCS,C2,C3,C4,C5,C6"""

        if self.symmetryOp:
            symmCode = self.symmCodePulldown.getSelected()
            self.symmetryOp.symmetryCode = symmCode

    def setSegLength(self, event):

        value = self.segLengthEntry.get() or 1
        self.symmetryOp.segmentLength = value

    def setChains(self, obj):

        if self.symmetryOp and obj:
            codes = self.setChainMulti.options
            segment = self.symmetryOp.findFirstSegment()
            values = self.setChainMulti.get()

            if segment: seqId0 = segment.firstSeqId
            else: seqId0 = 1

            for i in range(len(values)):
                segment = self.symmetryOp.findFirstSegment(chainCode=codes[i])

                if segment and not values[i]: segment.delete()
                elif values[i] and not segment:
                    chain = self.molSystem.findFirstChain(code=codes[i])
                    residue = chain.findFirstResidue(seqid=seqId0)

                    if residue: seqId = seqId0
                    else:
                        residue = chain.sortedResidues()[0]
                        seqId = residue.seqId

                    residue2 = chain.findFirstResidue(
                        seqid=seqId + self.symmetryOp.segmentLength)
                    if not residue2:
                        residue2 = chain.sortedResidues()[-1]
                        self.symmetryOp.segmentLength = (residue2.seqId -
                                                         seqId) + 1

                    segment = self.symmetryOp.newSegment(chainCode=codes[i],
                                                         firstSeqId=seqId)

        self.symmetryMatrix.keyPressEscape()

    def setSegments(self, obj):

        if self.symmetryOp and obj:
            segments = self.symmetryOp.sortedSegments()
            values = self.setSegmentMulti.get()

            for i in range(len(values)):
                seqCode = values[i]
                chain = self.molSystem.findFirstChain(
                    code=segments[i].chainCode)
                residue = chain.findFirstResidue(seqCode=seqCode)

                if residue: seqId = residue.seqId
                if segments[i].firstSeqId != seqId:
                    segments[i].delete()
                    segments[i] = self.symmetryOp.newSegment(
                        chainCode=chain.code, firstSeqId=seqId)

        self.symmetryMatrix.keyPressEscape()

    def selectSymmetry(self, obj, row, col):

        self.symmetryOp = obj

    def addSymmOp(self):

        if self.molSystem:
            if not self.symmetrySet:
                self.symmetrySet = self.molSystem.findFirstMolSystemSymmetrySet(
                )

            if not self.symmetrySet:
                objGen = self.project.newMolSystemSymmetrySet
                self.symmetrySet = objGen(symmetrySetId=1,
                                          molSystem=self.molSystem)

            segLen = len(self.molSystem.findFirstChain().residues)
            symmetry = self.symmetrySet.newSymmetry(segmentLength=segLen)

    def removeSymmOp(self):

        if self.symmetryOp: self.symmetryOp.delete()

    def toggleSingleMolecule(self, boolean):

        self.singleMolecule = not boolean
        self.updateMolSystems()
        self.updateMolecules()

    def setMolecules(self, molecules):

        self.molecules = molecules
        if self.symmetrySet:
            for symmetryOp in self.symmetrySet.symmetries:
                for segment in symmetryOp.segments:
                    chain = self.molSystem.findFirstChain(
                        code=segment.chainCode)
                if chain and (chain.molecule not in molecules):
                    segment.delete()

    def selectMolecule(self, index, name):

        self.setMolecules(self.getMolecules()[index])
        self.updateSymmetries()

    def getMolecules(self):

        counts = {}
        moleculesList = []

        for chain in self.molSystem.chains:
            molecule = chain.molecule
            counts[molecule] = counts.get(molecule, 0) + 1

        molecules = counts.keys()
        if self.singleMolecule:
            for molecule in counts:
                if counts[molecule] > 1: moleculesList.append([
                        molecule,
                ])

        elif molecules:
            molecules = counts.keys()
            n = len(molecules)
            moleculesList.append([
                molecules[0],
            ])

            if n > 1:
                moleculesList.append([
                    molecules[1],
                ])
                moleculesList.append([molecules[0], molecules[1]])

            if n > 2:
                moleculesList.append([
                    molecules[2],
                ])
                moleculesList.append([molecules[1], molecules[2]])
                moleculesList.append(
                    [molecules[0], molecules[1], molecules[2]])

            if n > 3:
                moleculesList.append([
                    molecules[3],
                ])
                moleculesList.append([molecules[0], molecules[3]])
                moleculesList.append([molecules[1], molecules[3]])
                moleculesList.append([molecules[2], molecules[3]])
                moleculesList.append(
                    [molecules[0], molecules[1], molecules[3]])
                moleculesList.append(
                    [molecules[0], molecules[2], molecules[3]])
                moleculesList.append(
                    [molecules[1], molecules[2], molecules[3]])
                moleculesList.append(
                    [molecules[0], molecules[1], molecules[2], molecules[3]])

        return moleculesList

    def updateMolecules(self):

        names = []
        index = -1
        moleculesList = self.getMolecules()
        if moleculesList:
            if self.molecules not in moleculesList:
                self.setMolecules(moleculesList[0])
                self.symmetrySet = self.molSystem.findFirstMolSystemSymmetrySet(
                )

            index = moleculesList.index(self.molecules)

            names = []
            for molecules in moleculesList:
                names.append(','.join([mol.name for mol in molecules]))

        else:
            self.molecules = []

        self.moleculePulldown.setup(names, index)

    def selectMolSystem(self, index, name):

        self.molSystem = self.getMolSystems()[index]
        self.symmetrySet = self.molSystem.findFirstMolSystemSymmetrySet()
        self.updateSymmetries()

    def getMolSystems(self):

        molSystems = []
        for molSystem in self.project.sortedMolSystems():
            n = len(molSystem.chains)

            if self.singleMolecule and (n > 1): molSystems.append(molSystem)
            elif n > 0: molSystems.append(molSystem)

        return molSystems

    def updateMolSystems(self):

        names = []
        index = -1
        molSystems = self.getMolSystems()

        if molSystems:
            if self.molSystem not in molSystems: self.molSystem = molSystems[0]

            index = molSystems.index(self.molSystem)
            names = [ms.code for ms in molSystems]
        else:
            self.molSystem = None

        self.molSystemPulldown.setup(names, index)

    def updateSymmetriesAfter(self, obj=None):

        if self.waiting: return
        else:
            self.waiting = True
            self.after_idle(self.updateSymmetries)

    def updateSymmetries(self):

        textMatrix = []
        objectList = []
        if self.symmetrySet:
            for symmetryOp in self.symmetrySet.symmetries:
                chains = []
                segments = []
                length = symmetryOp.segmentLength

                for segment in symmetryOp.sortedSegments():
                    code = segment.chainCode
                    chain = self.molSystem.findFirstChain(code=code)

                    if chain:
                        chains.append(code)
                        seqId = segment.firstSeqId
                        residue1 = chain.findFirstResidue(seqId=seqId)
                        residue2 = chain.findFirstResidue(seqId=seqId +
                                                          length - 1)
                        segments.append(
                            '%s:%d-%d' %
                            (code, residue1.seqCode, residue2.seqCode))

                datum = [
                    symmetryOp.serial, symmetryOp.symmetryCode, length,
                    '\n'.join(chains), '\n'.join(segments)
                ]
                objectList.append(symmetryOp)
                textMatrix.append(datum)

        self.symmetryMatrix.update(objectList=objectList,
                                   textMatrix=textMatrix)
        self.waiting = False

    def destroy(self):

        self.notify(self.unregisterNotify)
        BasePopup.destroy(self)
class AnnealingSettingsTab(object):
    '''This class describes the tab in the GUI where the user
       can change setting that govern the monte carlo / annleaing
       procedure. This also includes which information from the ccpn
       analysis project is used and which information is
       ignored. This includes:
           * present sequential assignments
           * tentative assignments
           * amino acid type information
           * whether to include untyped spin systems
           * assignments to peak dimensions
       ALso the chain can be selected here.
       Furthermore the user can set the temperature
       regime of the annealing, the amount of times the procedure
       is repeated to obtain statistics. The fraction of peaks
       that is left out in each run to diversify the results,
       the treshhold score for amino acid typing and the treshhold
       collabelling for a peak to be expected.
    '''

    def __init__(self, parent, frame):
        '''Init. args: parent: the guiElement that this
                               tab is part of.
                       frame:  the frame this part of the
                               GUI lives in.
        '''

        self.guiParent = parent
        self.frame = frame
        self.project = parent.project
        self.nmrProject = parent.nmrProject

        self.minIsoFrac = 0.1
        self.leavePeaksOutFraction = 0.0
        self.minTypeScore = 1.0
        self.chain = None
        self.amountOfRepeats = 10
        self.amountOfSteps = 10000
        self.acceptanceConstantList = [0.0, 0.01, 0.015, 0.022,
                                       0.033, 0.050, 0.075, 0.113,
                                       0.170, 0.256, 0.384, 0.576,
                                       0.864, 1.297, 1.946, 2.919,
                                       4.378, 6.568, 9.852, 14.77,
                                       22.16, 33.25]
        self.energyDataSets = [[]]
        self.residues = []
        self.body()

    def body(self):
        '''describes the body of this tab. It bascically consists
           of some field to fill out for the user at the top and
           a ScrolledGraph that shows the progess of the annealing
           procedure a the bottom.
        '''

        frame = self.frame

        # frame.expandGrid(13,0)
        frame.expandGrid(15, 1)
        row = 0

        text = 'Calculate Assignment Suggestions'
        command = self.runCalculations
        self.startButton = Button(frame, command=command, text=text)
        self.startButton.grid(row=row, column=0, sticky='nsew', columnspan=2)

        row += 1

        Label(frame, text='Amount of runs: ', grid=(row, 0))
        tipText = 'The amount of times the whole optimization procedure is performed, each result is safed'
        self.repeatEntry = IntEntry(frame, grid=(row, 1), width=7, text=10,
                                    returnCallback=self.updateRepeatEntry,
                                    tipText=tipText, sticky='nsew')
        self.repeatEntry.bind('<Leave>', self.updateRepeatEntry, '+')

        row += 1

        Label(frame, text='Temperature regime: ', grid=(row, 0))
        tipText = 'This list of numbers govern the temperature steps during the annealing, every number represents 1/(kb*t), where kb is the Boltzmann constant and t the temperature of one step.'
        self.tempEntry = Entry(frame, text=map(str, self.acceptanceConstantList), width=64,
                               grid=(row, 1), isArray=True, returnCallback=self.updateAcceptanceConstantList,
                               tipText=tipText, sticky='nsew')

        row += 1

        Label(frame, text='Amount of attempts per temperature:', grid=(row, 0))
        tipText = 'The amount of attempts to switch the position of two spinsystems in the sequence are performed for each temperature point'
        self.NAStepEntry = IntEntry(frame, grid=(row, 1), width=7, text=10000,
                                    returnCallback=self.updateStepEntry,
                                    tipText=tipText, sticky='nsew')
        self.NAStepEntry.bind('<Leave>', self.updateStepEntry, '+')

        row += 1

        Label(frame, text='Fraction of peaks to leave out:', grid=(row, 0))
        tipText = 'In each run a fraction of the peaks can be left out of the optimization, thereby increasing the variability in the outcome and reducing false negatives. In each run this will be different randomly chosen sub-set of all peaks. 0.1 (10%) can be a good value.'
        self.leaveOutPeaksEntry = FloatEntry(frame, grid=(row, 1), width=7, text=0.0,
                                             returnCallback=self.updateLeavePeaksOutEntry,
                                             tipText=tipText, sticky='nsew')
        self.leaveOutPeaksEntry.bind(
            '<Leave>', self.updateLeavePeaksOutEntry, '+')

        row += 1

        Label(frame, text='Minmal amino acid typing score:', grid=(row, 0))
        tipText = 'If automatic amino acid typing is selected, a cut-off value has to set. Every amino acid type that scores higher than the cut-off is taken as a possible type. This is the same score as can be found under resonance --> spin systems --> predict type. Value should be between 0 and 100'
        self.minTypeScoreEntry = FloatEntry(frame, grid=(row, 1), width=7, text=1.0,
                                            returnCallback=self.updateMinTypeScoreEntry,
                                            tipText=tipText, sticky='nsew')
        self.minTypeScoreEntry.bind(
            '<Leave>', self.updateMinTypeScoreEntry, '+')

        row += 1

        Label(frame, text='Minimal colabelling fraction:', grid=(row, 0))
        tipText = 'The minimal amount of colabelling the different nuclei should have in order to still give rise to a peak.'
        self.minLabelEntry = FloatEntry(frame, grid=(row, 1), width=7, text=0.1,
                                        returnCallback=self.updateMinLabelEntry,
                                        tipText=tipText, sticky='nsew')
        self.minLabelEntry.bind('<Leave>', self.updateMinLabelEntry, '+')

        row += 1

        Label(frame, text='Use sequential assignments:', grid=(row, 0))
        tipText = 'When this option is select the present sequential assignments will be kept in place'
        self.useAssignmentsCheck = CheckButton(
            frame, selected=True, tipText=tipText, grid=(row, 1))

        row += 1

        Label(frame, text='Use tentative assignments:', grid=(row, 0))
        tipText = 'If a spin system has tentative assignments this can be used to narrow down the amount of possible sequential assignments.'
        self.useTentativeCheck = CheckButton(
            frame, selected=True, tipText=tipText, grid=(row, 1))

        row += 1

        Label(frame, text='Use amino acid types:', grid=(row, 0))
        tipText = 'Use amino acid types of the spin systems. If this option is not checked the spin systems are re-typed, only resonance names and frequencies are used'
        self.useTypeCheck = CheckButton(
            frame, selected=True, tipText=tipText, grid=(row, 1))

        row += 1

        Label(frame, text='Include untyped spin systems:', grid=(row, 0))
        tipText = 'Also include spin system that have no type information. Amino acid typing will be done on the fly.'
        self.useAlsoUntypedSpinSystemsCheck = CheckButton(
            frame, selected=True, tipText=tipText, grid=(row, 1))

        row += 1

        Label(frame, text='Use dimensional assignments:', grid=(row, 0))
        tipText = 'If one or more dimensions of a peak is already assigned, assume that this assignment is the only option. If not the check the program will consider all possibilities for the assignment of the dimension.'
        self.useDimensionalAssignmentsCheck = CheckButton(
            frame, selected=True, tipText=tipText, grid=(row, 1))

        row += 1

        Label(frame, text='Chain:', grid=(row, 0))
        self.molPulldown = PulldownList(
            frame, callback=self.changeMolecule, grid=(row, 1))
        self.updateChains()

        row += 1

        Label(frame, text='Residue ranges: ', grid=(row, 0))
        tipText = 'Which residues should be included. Example: "10-35, 62-100, 130".'
        self.residueRangeEntry = Entry(frame, text=None, width=64,
                                       grid=(row, 1), isArray=True, returnCallback=self.updateResidueRanges,
                                       tipText=tipText, sticky='nsew')
        self.updateResidueRanges(fromChain=True)

        row += 1

        self.energyPlot = ScrolledGraph(frame, symbolSize=2, width=600,
                                        height=200, title='Annealing',
                                        xLabel='temperature step', yLabel='energy')
        self.energyPlot.grid(row=row, column=0, columnspan=2, sticky='nsew')

    def runCalculations(self):
        '''Run all calculations. Also triggers the disabling of
           some buttons and fields.
        '''

        self.startButton.disable()
        self.disableIllegalButtonsAfterPrecalculations()
        self.guiParent.connector.runAllCalculations()
        self.startButton.configure(text='More runs')
        self.startButton.enable()

    def disableIllegalButtonsAfterPrecalculations(self):
        '''Disable buttons and field the user can not alter
           any longer after the model is set up and the
           'pre-calculations' have finished.
           This is done because this part of the calculation
           should only be run once. All settings that would
           be changed after this point will not have any influence.
        '''

        illegalButtons = [self.minTypeScoreEntry, self.minLabelEntry,
                          self.useAlsoUntypedSpinSystemsCheck, self.useAssignmentsCheck,
                          self.useTypeCheck, self.useDimensionalAssignmentsCheck,
                          self.useTentativeCheck]

        for illegalButton in illegalButtons:
            illegalButton.configure(state='disabled')

        self.molPulldown.disable()

    def getChainName(self, chain):
        '''Get the name for a chain.
               args: chain: ccpn analysis
                            chain object
               returns: chain name
        '''

        return '%s:%s (%s)' % (chain.molSystem.code, chain.code, chain.molecule.molType)

    def getChains(self):
        '''Get all chains present in the project.
               returns: list of ccpn analysis chain objects
        '''
        chains = []
        if self.project:
            for molSystem in self.project.sortedMolSystems():
                for chain in molSystem.sortedChains():
                    if chain.residues:
                        chains.append(chain)

        return chains

    def updateChains(self, *opt):
        '''Updates the list of chains if a new one is added
           to or deleted from the project. Updates the
           pull down list where a chain can be selected.
        '''

        index = 0
        texts = []
        chains = self.getChains()
        chain = self.chain

        if chains:
            if chain not in chains:
                chain = chains[0]

            texts = [self.getChainName(c) for c in chains]
            index = chains.index(chain)

        else:
            chain = None

        self.molPulldown.setup(texts, chains, index)

        if chain is not self.chain:
            self.chain = chain

    def changeMolecule(self, chain):
        '''Select a molecular chain.'''

        if chain is not self.chain:
            self.chain = chain
            self.updateResidueRanges(fromChain=True)

    def updateStepEntry(self, event=None):
        '''Update the value and entry that sets the amount of
           steps per temperature point.
        '''

        value = self.NAStepEntry.get()
        if value == self.amountOfSteps:
            return
        if value < 1:
            self.NAStepEntry.set(1)
            self.amountOfSteps = 1
        else:
            self.amountOfSteps = value
            self.NAStepEntry.set(value)

    def updateRepeatEntry(self, event=None):
        '''Update the value and entry of that sets
           the amount of times the whole annealing
           procedure is repeated in order
           to obtain statistics.
        '''

        value = self.repeatEntry.get()

        if value == self.amountOfRepeats:
            return
        if value < 1:
            self.repeatEntry.set(1)
            self.amountOfRepeats = 1
        else:
            self.amountOfRepeats = value
            self.repeatEntry.set(value)

    def updateMinTypeScoreEntry(self, event=None):
        '''Updates the value and the entry for the
           treshhold value for amino acid typing.
        '''

        value = self.minTypeScoreEntry.get()

        if value == self.minTypeScore:
            return
        if value < 0:
            self.minTypeScoreEntry.set(0.0)
            self.minTypeScore = 0.0
        elif value > 100:
            self.minTypeScoreEntry.set(100.0)
            self.minTypeScore = 100.0
        else:
            self.minTypeScoreEntry.set(value)
            self.minTypeScore = value

    def updateMinLabelEntry(self, event=None):
        '''Updates the minimum colabelling fraction
           for which a peak is expected to be present
           in the spectra.
        '''

        value = self.minLabelEntry.get()

        if value == self.minIsoFrac:
            return
        if value < 0:
            self.minIsoFrac = 0.0
            self.minLabelEntry.set(0.0)
        elif value > 1:
            self.minIsoFrac = 1.0
            self.minLabelEntry.set(1.0)
        else:
            self.minIsoFrac = value
            self.minLabelEntry.set(value)

    def updateLeavePeaksOutEntry(self, event=None):
        '''Updates the value and entry of the fraction
           of peaks that should be left out in each
           run in order to diversify the results.
        '''

        value = self.leaveOutPeaksEntry.get()

        if value == self.leavePeaksOutFraction:
            return
        if value < 0:
            self.leavePeaksOutFraction = 0.0
            self.leaveOutPeaksEntry.set(0.0)
        elif value > 1:
            self.leavePeaksOutFraction = 1.0
            self.leaveOutPeaksEntry.set(1.0)
        else:
            self.leavePeaksOutFraction = value
            self.leaveOutPeaksEntry.set(value)

    def updateAcceptanceConstantList(self, event=None):
        '''Updates the list with constants that are used
           during the monte carlo procedure to decide whether
           a changed is accepted or not.
        '''

        acList = self.tempEntry.get()
        newList = []

        for constant in acList:

            try:

                number = float(constant)
                newList.append(number)

            except ValueError:

                string = constant + \
                    ' in temperature constants is not a number.'

                showWarning('Not A Number', string, parent=self.guiParent)

                return False

        self.acceptanceConstantList = newList

        return True

    def updateResidueRanges(self, event=None, fromChain=False):

        self.residues = set()

        subRanges = self.residueRangeEntry.get()
        if not subRanges or fromChain:
            self.residues = set(self.chain.residues)
            residues = self.chain.sortedResidues()
            text = '{}-{}'.format(residues[0].seqCode, residues[-1].seqCode)
            self.residueRangeEntry.set(text=text)
            return

        for subRange in subRanges:
            indeces = subRange.split('-')
            start = int(indeces[0])
            stop = int(indeces[-1]) + 1
            for seqCode in range(start, stop):
                residue = self.chain.findFirstResidue(seqCode=seqCode)
                if not residue:
                    showWarning('Residue out of range.',
                                'There is no residue at position {}'.format(seqCode),
                                parent=self.guiParent)
                    self.residues = set()
                    return
                self.residues.add(residue)

    def addEnergyPoint(self, energy, time):
        '''Adds a point to the graph that shows the progress
           of the annealling procedure.
               args: energy: the y-value
                     time:   the x-value
        '''

        point = (time, energy * -1)

        # This means one run has finished
        if len(self.energyDataSets[-1]) / (len(self.acceptanceConstantList) + 1):

            self.energyDataSets.append([point])

        else:

            self.energyDataSets[-1].append(point)

        colors = colorSeries
        Ncolors = len(colors)
        NdataSets = len(self.energyDataSets)
        colorList = (NdataSets / Ncolors) * colors + \
            colors[:NdataSets % Ncolors]
        self.energyPlot.update(dataSets=self.energyDataSets,
                               dataColors=colorList)

        # Forcing the graph to draw, eventhough calculations
        # are still running. Only do this with high numbers of
        # steps, otherwise drawing takes longer than annealling.
        if self.amountOfSteps >= 100000:

            self.energyPlot.draw()
class EditContourLevelsPopup(BasePopup):
    """
  **Change Levels in Spectrum Contour Displays**
  
  This popup window is used to specify which contour levels (lines of constant
  intensity) are drawn for spectra within the spectrum windows of Analysis. A
  spectrum can have both positive and negative levels, and the user can set
  these on an individual basis or as a regular incremental (usually geometric)
  series. It should be noted that spectrum contour colours, which may be
  different for positive and negative levels, is set elsewhere, in the main
  Spectra_ table.

  In normal operation the user first selects the spectrum to change the contours
  for in the upper left pulldown menu. Although contour levels are specified 
  for individual spectra, if there are several spectra that share the same
  levels (for example if they form a series of some kind) then the user may
  spread the contour level information from one to many via the [Propagate
  Contours] function within the "Display Options" of the main Spectra_ table.
  The user may also affect the contour levels of multiple spectra by changing
  the "Global scale" settings. This global scale will affect *all spectra* in
  the project; the scale value multiplies all of the contour levels for all
  spectra, although in practice it is rarely changed. Using a global scale gives
  the advantage of being able to specify smaller numbers for the levels. 

  The individual contour levels for a spectrum are listed in the "positive
  levels" and "Negative levels" fields. They are multiplied by the global scale
  factor when they are used for spectrum display. The user may type in values
  for the levels directly into these field, or they can be filled using the
  "Auto contour levels" mechanism. If manual levels are specified the user
  should press [Apply manual Levels] when done to commit the changes and see the
  results. The [Apply Auto Levels] function differs in that it always applies a
  regular series of levels, according to the settings, and these will overwrite
  any manual specifications when committed; the actual levels applied are always
  displayed in the levels field.

  The setting of "Auto" levels using a series involves choosing a base level,
  which represents the first contour (closest to zero). This base level is
  usually set according to the level of noise in the spectrum. Typically it is a
  value just above most of the noise, so the noise is not seen, but it may be
  set lower in some instances to show weak signals. The base level applies to
  both positive and negative contour series, although for the negative side it
  is naturally used with negative sign. The user sets how many levels there should be in total; the subsequent levels
  start from the base level and move away from zero. Normally this involves a
  geometric series, with a constant multiplication factor applied to a value to
  generate the next in the series. This factor is set in the "Level multiplier",
  although some of the commonly used values are quickly set via the adjacent
  buttons. Under a few special circumstances it is helpful to  have a constant
  difference  between levels, in which case "Add levels" may be used instead of
  "Multiply levels". 

  **Caveats & Tips**

  Often it is useful to initially setup contours using an "Auto" series, but
  then manually remove some of the levels. For example having all the positive
  levels but only the first negative level may be helpful to show where peaks
  are truncated.

  Note this system sets the levels that would be used to make any contour files
  (an optional way of working, especially for 4D spectra etc.). However,
  once contour files are made then this system will not affect the levels
  within the files.

  .. _Spectra: EditSpectrumPopup.html

  """
    def __init__(self, parent, *args, **kw):

        BasePopup.__init__(self,
                           parent=parent,
                           title='Spectrum Contour Levels',
                           **kw)

    def body(self, guiFrame):

        self.doUpdateForm = True
        self.spectrum = None

        guiFrame.grid_columnconfigure(1, weight=1)

        row = 0

        specFrame = LabelFrame(guiFrame, text='Spectrum', grid=(row, 0))

        tipText = 'The spectrum for which you are setting the contour levels'
        self.expt_spectrum = PulldownList(specFrame,
                                          grid=(0, 0),
                                          tipText=tipText,
                                          callback=self.setSpectrumDetails)

        tipText = 'Open the edit spectrum table to view and change other properties associated with the selected spectrum'
        button = Button(specFrame,
                        text='Spectrum Properties',
                        command=self.editProperties,
                        borderwidth=1,
                        grid=(1, 0),
                        sticky='ew',
                        tipText=tipText)

        globalFrame = LabelFrame(guiFrame, text='Global scale')
        globalFrame.grid(row=row, column=1, sticky='nsew')
        globalFrame.grid_columnconfigure(0, weight=1)

        tipText = 'The value by which all contour levels in all spectra are multiplied; to give the actual contour level in terms of the stored spectrum data'
        self.global_entry = FloatEntry(
            globalFrame,
            width=10,
            tipText=tipText,
            text=self.analysisProject.globalContourScale,
            returnCallback=self.applyAuto,
            grid=(0, 0),
            gridSpan=(1, 2),
            sticky='ew')

        tipText = 'Divide the global scaling factor by two; moving all contour for all spectra levels closer to zero'
        button = Button(globalFrame,
                        borderwidth=1,
                        text='/2',
                        sticky='ew',
                        tipText=tipText,
                        command=lambda: self.changeGlobalScale(0.5),
                        grid=(0, 2))

        tipText = 'Multiple the global scaling factor by two; moving all contour for all spectra levels away from zero'
        button = Button(globalFrame,
                        borderwidth=1,
                        text='*2',
                        sticky='ew',
                        tipText=tipText,
                        command=lambda: self.changeGlobalScale(2.0),
                        grid=(0, 3))

        tipText = 'Set the global contour scale for all spectra to the default of 100,000'
        button = Button(globalFrame,
                        borderwidth=1,
                        text='10^5',
                        tipText=tipText,
                        command=self.defaultGlobalScale,
                        grid=(1, 0))

        tipText = 'Click to decrease "-" or increase "+" the global contour scale by small amounts'
        frame = ValueRamp(globalFrame,
                          callback=self.changeGlobalScale,
                          tipText=tipText)
        frame.grid(row=1, column=1, columnspan=3, sticky='e')

        row += 1
        self.autoFrame = LabelFrame(guiFrame, text='Auto contour levels')
        self.autoFrame.grid(row=row, column=0, columnspan=5, sticky='nsew')
        self.autoFrame.grid_columnconfigure(1, weight=1)
        frow = 0

        label = Label(self.autoFrame,
                      text='Base level:',
                      grid=(frow, 0),
                      sticky='e')

        tipText = 'The first contour level (closest to zero) for an automated series of levels: the start of a geometric or arithmetic series defining levels'
        self.base_entry = FloatEntry(self.autoFrame,
                                     returnCallback=self.applyAuto,
                                     width=10,
                                     grid=(frow, 1),
                                     sticky='ew',
                                     tipText=tipText)

        command = lambda: self.changeBaseLevel(0.5)
        tipText = 'Lower the base contour level so that it is half of the previous value; moving the series of contour levels closer to zero'
        button = Button(self.autoFrame,
                        borderwidth=1,
                        text='/2',
                        tipText=tipText,
                        command=command,
                        grid=(frow, 2),
                        sticky='ew')

        command = lambda: self.changeBaseLevel(2.0)
        tipText = 'Raise the base contour level so that it is double the previous value; moving the series of contour levels further from zero'
        button = Button(self.autoFrame,
                        borderwidth=1,
                        text='*2',
                        tipText=tipText,
                        command=command,
                        grid=(frow, 3),
                        sticky='ew')

        tipText = 'Click to decrease "-" or increase "+" the base contour level by small amounts'
        frame = ValueRamp(self.autoFrame,
                          callback=self.changeBaseLevel,
                          tipText=tipText)
        frame.grid(row=frow, column=4, columnspan=3, sticky='ew')

        frow += 1
        label = Label(self.autoFrame,
                      text='Number of levels:',
                      grid=(frow, 0),
                      sticky='e')
        #self.numberEntry = IntEntry(guiFrame, text=numberLevels,
        #                            returnCallback=self.applyAuto)
        tipText = 'The number of contour levels to make in the automated series'
        self.numberEntry = IntEntry(self.autoFrame,
                                    returnCallback=self.applyAuto,
                                    width=10,
                                    grid=(frow, 1),
                                    sticky='ew',
                                    tipText=tipText)

        command = lambda w=-1: self.changeNumberLevels(w)
        tipText = 'Decrease the number of contour levels in the series by one'
        button = Button(self.autoFrame,
                        borderwidth=1,
                        text='-1',
                        tipText=tipText,
                        command=command,
                        grid=(frow, 2),
                        sticky='ew')

        command = lambda w=1: self.changeNumberLevels(w)
        tipText = 'Increase the number of contour levels in the series by one'
        button = Button(self.autoFrame,
                        borderwidth=1,
                        text='+1',
                        tipText=tipText,
                        command=command,
                        grid=(frow, 3),
                        sticky='ew')

        n = 4
        for w in (5, 10, 15, 20):
            tipText = 'Set the number of contour levels in the series to %d' % w
            command = lambda w=w: self.setNumberLevels(w)
            button = Button(self.autoFrame,
                            borderwidth=1,
                            text='%d' % w,
                            command=command,
                            grid=(frow, n),
                            sticky='ew',
                            tipText=tipText)
            n += 1

        frow += 1
        self.change_label = Label(self.autoFrame,
                                  text='%s:' % multiplier_text,
                                  grid=(frow, 0),
                                  sticky='e')

        tipText = 'The multiplication factor (or increment if adding levels) to repetitively apply to the base level to generate the series of levels'
        self.change_entry = FloatEntry(self.autoFrame,
                                       returnCallback=self.applyAuto,
                                       width=10,
                                       grid=(frow, 1),
                                       sticky='ew',
                                       tipText=tipText)

        self.change_level_buttons = []
        n = 2
        for w in multiplier_changes:
            tipText = 'Set the automated series multiplication factor or increment to %f' % w
            command = lambda w=w: self.setChange(w)
            button = Button(self.autoFrame,
                            borderwidth=1,
                            text=str(w),
                            command=command,
                            grid=(frow, n),
                            sticky='ew',
                            tipText=tipText)
            self.change_level_buttons.append(button)
            n += 1

        frow += 1
        frame = Frame(self.autoFrame,
                      grid=(frow, 0),
                      gridSpan=(1, 7),
                      sticky='ew')

        tipTexts = [
            'Toggles whether positive contour levels from the automated series will be used. Overrides any manual settings',
            'Toggles whether negative contour levels from the automated series will be used. Overrides any manual settings'
        ]
        entries = ('Positive', 'Negative')
        selected = (True, False)
        self.which_buttons = CheckButtons(frame,
                                          entries,
                                          selected=selected,
                                          select_callback=self.applyAuto,
                                          grid=(0, 0),
                                          sticky='w',
                                          tipTexts=tipTexts)

        tipTexts = [
            'Set the contour level generation to use a geometric series, starting from the base level and using the specified factor',
            'Set the contour level generation to use an arithmetic series, starting from the base level and using the specified increment'
        ]
        entries = ('Multiply levels', 'Add levels')
        self.change_mode_buttons = RadioButtons(
            frame,
            entries,
            grid=(0, 1),
            sticky='ew',
            select_callback=self.modeChanged,
            tipTexts=tipTexts)

        row += 1
        manualFrame = LabelFrame(guiFrame,
                                 text='Positive levels',
                                 grid=(row, 0),
                                 gridSpan=(1, 2))
        manualFrame.expandGrid(None, 0)
        tipText = 'The positive contour levels that will be used for the spectrum; filled in by the automation or set/modified manually'
        self.posLevelsEntry = FloatEntry(manualFrame,
                                         isArray=True,
                                         width=60,
                                         returnCallback=self.applyManual,
                                         tipText=tipText,
                                         grid=(0, 0),
                                         sticky='ew')

        row += 1
        manualFrame = LabelFrame(guiFrame,
                                 text='Negative levels',
                                 grid=(row, 0),
                                 gridSpan=(1, 2))
        manualFrame.expandGrid(None, 0)
        tipText = 'The negative contour levels that will be used for the spectrum; filled in by the automation or set/modified manually'
        self.negLevelsEntry = FloatEntry(manualFrame,
                                         isArray=True,
                                         width=60,
                                         returnCallback=self.applyManual,
                                         tipText=tipText,
                                         grid=(0, 0),
                                         sticky='ew')

        row += 1
        tipTexts = [
            'Set the spectrum contour levels, updating the display, to the automated series, ignoring any manual edits.',
            'Set the spectrum contour levels, updating the display, to the values displayed in the level entry fields'
        ]
        texts = ['Apply Auto Levels', 'Apply Manual Edits']
        commands = [self.applyAuto, self.applyManual]
        self.buttons = UtilityButtonList(guiFrame,
                                         texts=texts,
                                         commands=commands,
                                         helpUrl=self.help_url,
                                         grid=(row, 0),
                                         gridSpan=(1, 2),
                                         tipTexts=tipTexts)
        guiFrame.grid_rowconfigure(row, weight=1)

        self.curateNotifiers(self.registerNotify)
        self.update()

    def update(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.setSpectrumDetails(spectrum)

    def updateNotifier(self, *extra):

        self.update()

    def open(self):

        self.updateForm()
        BasePopup.open(self)

    def modeChanged(self, *entry):

        spectrum = self.spectrum
        if not spectrum or spectrum.isDeleted:
            return

        analysisSpectrum = spectrum.analysisSpectrum

        changeMode = self.change_mode_buttons.getIndex(
        ) and 'add' or 'multiply'
        if changeMode == 'add':
            text = adder_text
            changes = adder_changes
        else:
            text = multiplier_text
            changes = multiplier_changes

        self.change_label.set('%s:' % text)
        n = 0
        for button in self.change_level_buttons:
            w = changes[n]
            command = lambda w=w: self.setChange(w)
            button.config(command=command)
            button.setText(str(w))
            n = n + 1

        levelChanger = changes[2]
        self.doUpdateForm = False

        analysisSpectrum.autoLevelChanger = levelChanger
        analysisSpectrum.autoLevelMode = changeMode

        self.doUpdateForm = True
        self.setContourLevels()

    def defaultGlobalScale(self):

        self.global_entry.set(100000)
        self.applyAuto()

    def close(self):

        self.applyManual()
        BasePopup.close(self)

    def destroy(self):

        self.curateNotifiers(self.unregisterNotify)

        BasePopup.destroy(self)

    def curateNotifiers(self, notifyFunc):

        notifyFunc(self.updateContourLevels,
                   'ccpnmr.Analysis.AnalysisSpectrum', 'setPosLevels')
        notifyFunc(self.updateContourLevels,
                   'ccpnmr.Analysis.AnalysisSpectrum', 'setNegLevels')
        notifyFunc(self.updateForm, 'ccpnmr.Analysis.AnalysisSpectrum',
                   'setAutoLevelChanger')
        notifyFunc(self.updateForm, 'ccpnmr.Analysis.AnalysisSpectrum',
                   'setAutoLevelMode')
        notifyFunc(self.updateForm, 'ccpnmr.Analysis.AnalysisSpectrum',
                   'setAutoNumLevels')
        notifyFunc(self.updateForm, 'ccpnmr.Analysis.AnalysisSpectrum',
                   'setAutoBaseLevel')

        notifyFunc(self.updateForm, 'ccpnmr.Analysis.AnalysisProject',
                   'setGlobalContourScale')

        for clazz in ('Experiment', 'DataSource'):
            for func in ('__init__', 'delete', 'setName'):
                notifyFunc(self.updateNotifier, 'ccp.nmr.Nmr.%s' % clazz, func)

    def editProperties(self):

        self.parent.editSpectrum(self.spectrum)

    def updateForm(self, *extra):

        #print 'updateForm'
        if (not self.doUpdateForm):
            return

        self.global_entry.set(self.analysisProject.globalContourScale)
        spectrum = self.spectrum
        if spectrum and not spectrum.isDeleted:
            analysisSpectrum = spectrum.analysisSpectrum

            self.base_entry.set(analysisSpectrum.autoBaseLevel)
            self.numberEntry.set(analysisSpectrum.autoNumLevels)
            self.change_entry.set(analysisSpectrum.autoLevelChanger)

            if analysisSpectrum.autoLevelMode == 'add':
                i = 1
            else:
                i = 0

            self.change_mode_buttons.setIndex(i)

    def updateContourLevels(self, analysisSpectrum):

        spectrum = self.spectrum
        if spectrum and not spectrum.isDeleted:
            analysisSpectrum = spectrum.analysisSpectrum

            posLevels = list(analysisSpectrum.posLevels)
            negLevels = list(analysisSpectrum.negLevels)

            self.posLevelsEntry.set(posLevels)
            self.negLevelsEntry.set(negLevels)

            self.doUpdateForm = False
            updateSpectrumLevelParams(analysisSpectrum, posLevels, negLevels)

            self.doUpdateForm = True
            self.base_entry.set(analysisSpectrum.autoBaseLevel)
            self.numberEntry.set(analysisSpectrum.autoNumLevels)
            self.change_entry.set(analysisSpectrum.autoLevelChanger)

            self.setWhichLevels(spectrum)

            if analysisSpectrum.autoLevelMode == 'add':
                i = 1
            else:
                i = 0

            self.change_mode_buttons.setIndex(i)

    def setWhichLevels(self, spectrum):

        analysisSpectrum = spectrum.analysisSpectrum

        posLevels = analysisSpectrum.posLevels
        negLevels = analysisSpectrum.negLevels

        if posLevels:
            isSelected = True
        else:
            isSelected = False
        self.which_buttons.setIndexSelection(0, isSelected)

        if negLevels:
            isSelected = True
        else:
            isSelected = False

        self.which_buttons.setIndexSelection(1, isSelected)

    def setSpectrum(self, spectrum):

        if spectrum is not self.spectrum:
            self.update(spectrum)

        #if (spectrum and not spectrum.isDeleted):
        #  self.setWhichLevels(spectrum)

    speed_scale = 6.0
    speed_delay = 50  # msec

    def changeGlobalScale(self, multiplier):

        self.analysisProject.globalContourScale = multiplier * self.analysisProject.globalContourScale

    def changeBaseLevel(self, multiplier):

        spectrum = self.spectrum
        if (not spectrum or spectrum.isDeleted is True):
            return

        analysisSpectrum = spectrum.analysisSpectrum
        baseLevel = multiplier * analysisSpectrum.autoBaseLevel
        self.base_entry.set(baseLevel)
        self.doUpdateForm = False

        analysisSpectrum.autoBaseLevel = abs(baseLevel)

        self.doUpdateForm = True
        self.setContourLevels()

    def changeNumberLevels(self, change):

        spectrum = self.spectrum
        if not spectrum or spectrum.isDeleted is True:
            return

        analysisSpectrum = spectrum.analysisSpectrum
        numberLevels = analysisSpectrum.autoNumLevels + change
        self.numberEntry.set(numberLevels)
        self.doUpdateForm = False

        analysisSpectrum.autoNumLevels = numberLevels

        self.doUpdateForm = True
        self.setContourLevels()

    def setChange(self, levelChanger):

        spectrum = self.spectrum
        if not spectrum or spectrum.isDeleted:
            return

        self.doUpdateForm = False
        analysisSpectrum = spectrum.analysisSpectrum
        analysisSpectrum.autoLevelChanger = levelChanger
        self.doUpdateForm = True

        self.setContourLevels()

    def setNumberLevels(self, numberLevels):

        spectrum = self.spectrum
        if (not spectrum or spectrum.isDeleted is True):
            return

        self.doUpdateForm = False
        analysisSpectrum = spectrum.analysisSpectrum
        analysisSpectrum.autoNumLevels = numberLevels
        self.doUpdateForm = True

        self.setContourLevels()

    def setSpectrumDetails(self, spectrum):

        if spectrum is self.spectrum:
            return

        self.spectrum = spectrum
        if spectrum and not spectrum.isDeleted:
            analysisSpectrum = spectrum.analysisSpectrum

            posLevels = list(analysisSpectrum.posLevels)
            negLevels = list(analysisSpectrum.negLevels)

            self.posLevelsEntry.set(posLevels)
            self.negLevelsEntry.set(negLevels)

            self.doUpdateForm = False
            updateSpectrumLevelParams(analysisSpectrum, posLevels, negLevels)

            self.doUpdateForm = True
            self.base_entry.set(analysisSpectrum.autoBaseLevel)
            self.numberEntry.set(analysisSpectrum.autoNumLevels)
            self.change_entry.set(analysisSpectrum.autoLevelChanger)
            self.autoFrame.setText('Auto contour levels - %s:%s' %
                                   (spectrum.experiment.name, spectrum.name))
            self.setWhichLevels(spectrum)

        else:
            self.posLevelsEntry.set('')
            self.negLevelsEntry.set('')
            self.autoFrame.setText('Auto contour levels')

    def setContourLevels(self):

        spectrum = self.spectrum
        if not spectrum or spectrum.isDeleted is True:
            return

        try:
            analysisSpectrum = spectrum.analysisSpectrum
            baseLevel = analysisSpectrum.autoBaseLevel
            numberLevels = analysisSpectrum.autoNumLevels
            levelChanger = analysisSpectrum.autoLevelChanger
            changeMode = analysisSpectrum.autoLevelMode

            posLevels = []
            if self.which_buttons.isIndexSelected(0):
                posLevels.extend(
                    calcContourLevels(baseLevel, numberLevels, levelChanger,
                                      changeMode))

            negLevels = []
            if self.which_buttons.isIndexSelected(1):
                if changeMode == 'add':
                    levelChanger = -levelChanger
                negLevels.extend(
                    calcContourLevels(-baseLevel, numberLevels, levelChanger,
                                      changeMode))

            self.posLevelsEntry.set(posLevels)
            self.negLevelsEntry.set(negLevels)
            analysisSpectrum.posLevels = posLevels
            analysisSpectrum.negLevels = negLevels

        except Implementation.ApiError, e:
            showError('Contour levels error', e.error_msg, parent=self)
Exemple #10
0
class ViewNoeMatrix(BasePopup):
    """
  **Display a Density Matrix of Residue-Residue Contact Information**

  This popup window is based around the "Interaction Matrix" in the first tab
  and is designed to give a graphical representation of the residue to residue
  interactions that are present in NMR data. At present this informations comes
  from peak lists; to show assignment connectivities (e.g. NOE) and restraint
  lists; to show distance and H-bond restraints.

  To operate this system the user opens the "Peak Lists" and "Restraint Lists"
  tabs and toggles the "Use?" columns to enable or disable peak and restraint
  lists in the analysis. After pressing the [Draw] button at the top, the
  interaction matrix in the first tab is redrawn to display the connectivity
  information form the selected sources.

  The main interaction matrix is colour coded so that darker squares represent 
  a greater number of observed interactions. The coordinates of each square
  represents the intersection between two residues from the axes. The residues
  that are used on the X- and Y-axes many be specified via the "Residues" tab,
  both in terms of sequence range and molecular chain. To determine the precise
  residues that correspond to a given interaction the user can hover the mouse
  cursor over a small square in the matrix and the identities of the residues
  are displayed on the two axes.

  **Tips**

  If the interaction matrix display is too large or too small the size of
  the chart can be adjusted via the [+] and [-] buttons.

  A PostScript file of the density matrix can be saved via the right mouse menu
  of the matrix tab.
  """
    def __init__(self, parent, *args, **kw):

        self.guiParent = parent
        self.peakLists = []
        self.constraintLists = []
        self.constraintSet = None
        BasePopup.__init__(self,
                           parent=parent,
                           title="Chart : Residue Interaction Matrix",
                           **kw)

    def body(self, guiFrame):

        self.geometry('600x700')

        self.noeMatrix = None

        self.xMol = None
        self.yMol = None

        guiFrame.expandGrid(0, 0)

        tipTexts = [
            'The main colour density matrix that displays residue-residue interaction strength',
            'Selects which peak lists to show residue-residue interactions for',
            'Selects which distance restraint lists to show residue-residue interactions for',
            'Specifies which molecular chains and residue ranges to consider on the matrix axes'
        ]
        options = [
            'Interaction Matrix', 'Peak Lists', 'Restraint Lists', 'Residues'
        ]
        tabbedFrame = TabbedFrame(guiFrame,
                                  options=options,
                                  grid=(0, 0),
                                  tipTexts=tipTexts)
        frameA, frameB, frameC, frameD = tabbedFrame.frames

        #
        # Matrix
        #

        frameA.expandGrid(0, 0)

        self.noeMatrix = NoeMatrix(frameA,
                                   borderwidth=1,
                                   relief='flat',
                                   background='darkGrey',
                                   labelAxes=False)
        self.noeMatrix.grid(row=0, column=0, sticky='nsew')
        self.noeMatrix.updateAfter()

        #
        # Peak Lists
        #

        frameB.expandGrid(0, 0)

        tipTexts = [
            'The experiment:spectrum name of the peak list that may be considered',
            'The serial number of the peak lists within its spectrum',
            'Sets whether or not the peak list will be used as a source of residue interaction information'
        ]
        headingList = ['Spectrum', 'PeakList', 'Use?']
        editWidgets = [None, None, None]
        editGetCallbacks = [None, None, self.togglePeakList]
        editSetCallbacks = [None, None, None]
        self.peakMatrix = ScrolledMatrix(frameB,
                                         headingList=headingList,
                                         editSetCallbacks=editSetCallbacks,
                                         editGetCallbacks=editGetCallbacks,
                                         editWidgets=editWidgets,
                                         multiSelect=False,
                                         grid=(0, 0),
                                         tipTexts=tipTexts)

        #
        # Restraints
        #

        frameC.expandGrid(1, 1)

        label = Label(frameC, text='Restraint Set: ', grid=(0, 0))
        tipText = 'Selects which set of restraints to select restraint connectivity information from'
        self.constraintSetPulldown = PulldownList(frameC,
                                                  self.changeConstraintSet,
                                                  grid=(0, 1),
                                                  tipText=tipText)

        tipTexts = [
            'The serial number, within the restraint set, and name of the restraint list',
            'Whether the restraint list is a Distance restraint list or an H-Bond restraint list',
            'Sets whether or not the restraint list will be used as a source of residue interaction information'
        ]
        headingList = ['List', 'Type', 'Use?']
        editWidgets = [None, None, None]
        editGetCallbacks = [None, None, self.toggleConstraintList]
        editSetCallbacks = [None, None, None]
        self.constraintMatrix = ScrolledMatrix(
            frameC,
            headingList=headingList,
            editSetCallbacks=editSetCallbacks,
            editGetCallbacks=editGetCallbacks,
            editWidgets=editWidgets,
            multiSelect=False,
            grid=(1, 0),
            tipTexts=tipTexts,
            gridSpan=(1, 2))

        #
        # Residues
        #

        frameD.expandGrid(2, 5)

        label = Label(frameD, text='X axis: ', grid=(0, 0))
        label = Label(frameD, text='Y axis: ', grid=(1, 0))

        tipText = 'Selects which molecular chain to use as the sequence along the horizontal axis of the interaction matrix'
        self.xMolPulldown = PulldownList(frameD,
                                         callback=self.changeMolX,
                                         grid=(0, 1),
                                         tipText=tipText)
        tipText = 'Selects which molecular chain to use as the sequence along the vertical axis of the interaction matrix'
        self.yMolPulldown = PulldownList(frameD,
                                         callback=self.changeMolY,
                                         grid=(1, 1),
                                         tipText=tipText)

        tipText = 'Sets the number of the first residue that appears at the start (left) of the horizontal matrix axis'
        self.xEntryStart = IntEntry(frameD,
                                    text='',
                                    returnCallback=self.setMolRanges,
                                    width=5,
                                    grid=(0, 2),
                                    tipText=tipText)
        tipText = 'Sets the number of the first residue that appears at the start (bottom) of the vertical matrix axis'
        self.yEntryStart = IntEntry(frameD,
                                    text='',
                                    returnCallback=self.setMolRanges,
                                    width=5,
                                    grid=(1, 2),
                                    tipText=tipText)

        label = Label(frameD, text=' to ', grid=(0, 3))
        label = Label(frameD, text=' to ', grid=(1, 3))

        tipText = 'Sets the number of the last residue that appears at the end (right) of the horizontal matrix axis'
        self.xEntryStop = IntEntry(frameD,
                                   text='',
                                   returnCallback=self.setMolRanges,
                                   width=5,
                                   grid=(0, 4),
                                   tipText=tipText)
        tipText = 'Sets the number of the last residue that appears at the end (top) of the vertical matrix axis'
        self.yEntryStop = IntEntry(frameD,
                                   text='',
                                   returnCallback=self.setMolRanges,
                                   width=5,
                                   grid=(1, 4),
                                   tipText=tipText)

        #
        # Main
        #

        tipTexts = [
            'Using the selected peak lists, restraint lists and residue ranges draw a colour density residue interaction matrix',
            'Zoom in on the matrix view; make the boxes larger',
            'Zoom out on the matrix view; make the boxes smaller'
        ]
        commands = [self.updateNoes, self.zoomIn, self.zoomOut]
        texts = ['Draw', '[+]', '[-]']
        bottomButtons = UtilityButtonList(tabbedFrame.sideFrame,
                                          commands=commands,
                                          texts=texts,
                                          helpUrl=self.help_url,
                                          grid=(0, 0),
                                          sticky='e',
                                          tipTexts=tipTexts)

        self.updateConstraintSets()
        self.updateMols()
        self.update()
        #self.updateNoes()

        self.administerNotifiers(self.registerNotify)

    def administerNotifiers(self, notifyFunc):

        for func in (
                '__init__',
                'delete',
                'setName',
        ):
            notifyFunc(self.updatePeakLists, 'ccp.nmr.Nmr.DataSource', func)
            notifyFunc(self.updatePeakLists, 'ccp.nmr.Nmr.Experiment', func)
            notifyFunc(self.updatePeakLists, 'ccp.nmr.Nmr.PeakList', func)

        for func in ('__init__', 'delete'):
            notifyFunc(self.updateMols, 'ccp.molecule.MolSystem.MolSystem',
                       func)
            notifyFunc(self.updateMols, 'ccp.molecule.MolSystem.Chain', func)

        for func in ('__init__', 'delete'):
            notifyFunc(self.updateConstraintSets,
                       'ccp.nmr.NmrConstraint.NmrConstraintStore', func)

        for func in ('__init__', 'delete', 'setName'):
            for clazz in ('ccp.nmr.NmrConstraint.DistanceConstraintList',
                          'ccp.nmr.NmrConstraint.HBondConstraintList'):
                notifyFunc(self.updateConstraintLists, clazz, func)

    def open(self):

        self.updateConstraintSets()
        self.updateMols()
        self.update()
        BasePopup.open(self)

    def update(self):

        self.updatePeakLists()
        self.updateConstraintLists()

    def togglePeakList(self, peakList):

        if peakList in self.peakLists:
            self.peakLists.remove(peakList)
        else:
            self.peakLists.append(peakList)

        self.updatePeakLists()

    def toggleConstraintList(self, constraintList):

        if constraintList in self.constraintLists:
            self.constraintLists.remove(constraintList)
        else:
            self.constraintLists.append(constraintList)

        self.updateConstraintLists()

    def getMolKeys(self):

        names = []
        for molSystem in self.project.sortedMolSystems():
            for chain in molSystem.sortedChains():
                if chain.residues:
                    name = '%s:%s' % (molSystem.code, chain.code)
                    names.append((name, chain))

        return names

    def updateMols(self):

        molKeys = self.getMolKeys()
        names = [x[0] for x in molKeys]
        chains = [x[1] for x in molKeys]

        indexX = 0
        indexY = 0

        if chains:
            if self.xMol not in chains:
                self.xMol = chains[0]

            if self.yMol not in chains:
                self.yMol = chains[0]

            indexX = chains.index(self.xMol)
            indexY = chains.index(self.yMol)

        self.xMolPulldown.setup(names, chains, indexX)
        self.yMolPulldown.setup(names, chains, indexY)

    def changeMolX(self, chain):

        self.xMol = chain
        residues = chain.sortedResidues()
        self.xEntryStart.set(residues[0].seqCode)
        self.xEntryStop.set(residues[-1].seqCode)
        self.setMolRanges()

    def changeMolY(self, chain):

        self.yMol = chain
        residues = chain.sortedResidues()
        self.yEntryStart.set(residues[0].seqCode)
        self.yEntryStop.set(residues[-1].seqCode)
        self.setMolRanges()

    def getPeakLists(self):
        # tbd noesy only

        peakLists = []
        for experiment in self.project.currentNmrProject.sortedExperiments():
            for spectrum in experiment.sortedDataSources():
                if (spectrum.dataType
                        == 'processed') and (spectrum.numDim > 1):
                    #isotopes = getSpectrumIsotopes(spectrum)
                    #if isotopes.count('1H') > 1:
                    #  spectra.append(spectrum)
                    for peakList in spectrum.peakLists:
                        peakLists.append(peakList)

        return peakLists

    def zoomIn(self):

        z = self.noeMatrix.zoom
        self.noeMatrix.setZoom(z * 1.4)

    def zoomOut(self):

        z = self.noeMatrix.zoom
        self.noeMatrix.setZoom(z * 0.7)

    def changeConstraintSet(self, constraintSet):

        if constraintSet is not self.constraintSet:
            self.constraintSet = constraintSet
            self.updateConstraintLists()

    def updateConstraintSets(self, *opt):

        index = 0
        constraintSet = self.constraintSet
        constraintSets = self.nmrProject.sortedNmrConstraintStores()
        constraintSetNames = ['%d' % cs.serial for cs in constraintSets]

        if constraintSets:

            if constraintSet not in constraintSets:
                constraintSet = constraintSets[0]

            index = constraintSets.index(constraintSet)

        else:
            constraintSet = None

        self.constraintSetPulldown.setup(constraintSetNames, constraintSets,
                                         index)

        if self.constraintSet is not constraintSet:
            self.constraintSet = constraintSet
            self.updateConstraintLists()

    def getConstraintLists(self):

        constraintLists = []
        if self.constraintSet:
            for constraintList in self.constraintSet.constraintLists:
                if constraintList.className in ('DistanceConstraintList',
                                                'HBondConstraintList'):
                    constraintLists.append(constraintList)

        return constraintLists

    def updateConstraintLists(self, constraintList=None):

        if constraintList and (constraintList.parent
                               is not self.constraintSet):
            return

        textMatrix = []
        colorMatrix = []
        objectList = []

        if self.constraintSet:
            objectList = self.getConstraintLists()

            for constraintList in objectList:
                name = '%d:%s' % (constraintList.serial, constraintList.name)
                lType = constraintList.className[:-14]

                if constraintList in self.constraintLists:
                    isUsed = 'Yes'
                    colors = [None, None, '#C0FFC0']

                else:
                    isUsed = 'No'
                    colors = [None, None, None]

                datum = [name, lType, isUsed]
                textMatrix.append(datum)
                colorMatrix.append(colors)

        self.constraintMatrix.update(objectList=objectList,
                                     textMatrix=textMatrix,
                                     colorMatrix=colorMatrix)

    def updatePeakLists(self, *opt):

        textMatrix = []
        colorMatrix = []
        objectList = self.getPeakLists()
        for peakList in objectList:

            spectrum = peakList.dataSource
            experiment = spectrum.experiment

            if peakList in self.peakLists:
                isUsed = 'Yes'
                colors = [None, None, '#C0FFC0']

            else:
                isUsed = 'No'
                colors = [None, None, None]

            datum = [
                '%s:%s' % (experiment.name, spectrum.name), peakList.serial,
                isUsed
            ]
            textMatrix.append(datum)
            colorMatrix.append(colors)

        self.peakMatrix.update(objectList=objectList,
                               textMatrix=textMatrix,
                               colorMatrix=colorMatrix)

    def setMolRanges(self, *opt):

        xStart = self.xEntryStart.get() or None

        xStop = self.xEntryStop.get() or None

        yStart = self.yEntryStart.get() or None

        yStop = self.yEntryStop.get() or None

        #if xStop < xStart:
        #  (xStart,xStop) = (xStop,xStart)

        #if yStop < yStart:
        #  (yStart,yStop) = (yStop,yStart)

        if self.xMol and self.yMol:
            xChain = self.xMol
            yChain = self.yMol
            xResidues = xChain.sortedResidues()
            yResidues = yChain.sortedResidues()
            xFirst = xResidues[0].seqCode
            xLast = xResidues[-1].seqCode
            yFirst = yResidues[0].seqCode
            yLast = yResidues[-1].seqCode

            if xStart is None:
                xStart = xFirst
            if yStart is None:
                yStart = yFirst
            if xStop is None:
                xStop = xLast
            if yStop is None:
                yStop = yLast

            xStart = max(xStart, xFirst)
            yStart = max(yStart, yFirst)
            xStop = min(xStop, xLast)
            yStop = min(yStop, yLast)

        self.xEntryStart.set(xStart)
        self.yEntryStart.set(yStart)
        self.xEntryStop.set(xStop)
        self.yEntryStop.set(yStop)

        return (xStart, xStop, yStart, yStop)

    def updateNoes(self):

        #if not self.peakLists:
        #  return

        if not (self.noeMatrix and self.xMol and self.yMol):
            return

        xChain = self.xMol
        yChain = self.yMol

        (xStart, xStop, yStart, yStop) = self.setMolRanges()

        xResidues = []
        for residue in xChain.sortedResidues():
            if (residue.seqCode >= xStart) and (residue.seqCode <= xStop):
                xResidues.append(residue)

        yResidues = []
        for residue in yChain.sortedResidues():
            if (residue.seqCode >= yStart) and (residue.seqCode <= yStop):
                yResidues.append(residue)

        self.noeMatrix.peakLists = self.peakLists
        self.noeMatrix.restraintLists = self.constraintLists
        self.noeMatrix.xResidues = xResidues
        self.noeMatrix.yResidues = yResidues
        self.noeMatrix.updateAfter()
Exemple #11
0
class UpdateAdministratorPopup(BasePopup, UpdateAgent):
    def __init__(self,
                 parent,
                 serverLocation=UPDATE_SERVER_LOCATION,
                 serverDirectory=UPDATE_DIRECTORY,
                 dataFile=UPDATE_DATABASE_FILE):

        UpdateAgent.__init__(self,
                             serverLocation,
                             serverDirectory,
                             dataFile,
                             admin=1)

        self.fileTypes = [
            FileType('Python', ['*.py']),
            FileType('C', ['*.c']),
            FileType('All', ['*'])
        ]
        self.fileUpdate = None

        BasePopup.__init__(self,
                           parent=parent,
                           title='CcpNmr Update Administrator',
                           quitFunc=self.quit)

    def body(self, guiParent):

        guiParent.grid_columnconfigure(3, weight=1)

        self.commentsEntry = Entry(self)
        self.priorityEntry = IntEntry(self)

        row = 0
        label = Label(guiParent, text='Server location:')
        label.grid(row=row, column=0, sticky='w')

        location = ''
        uid = ''
        httpDir = ''
        subDir = ''
        version = 'None'

        if self.server:
            location, uid, httpDir, subDir = self.server.identity
            version = self.server.version or 'None'

        self.serverEntry = Entry(guiParent, text=location)
        self.serverEntry.grid(row=row, column=1, stick='w')

        label = Label(guiParent, text='User ID:')
        label.grid(row=row, column=2, sticky='w')
        self.uidEntry = Entry(guiParent, text=uid)
        self.uidEntry.grid(row=row, column=3, stick='w')

        row += 1
        label = Label(guiParent, text='HTTP directory:')
        label.grid(row=row, column=0, sticky='w')
        self.httpDirEntry = Entry(guiParent, text=httpDir)
        self.httpDirEntry.grid(row=row, column=1, stick='w')

        label = Label(guiParent, text='Sub-directory:')
        label.grid(row=row, column=2, sticky='w')
        self.subDirEntry = Entry(guiParent, text=subDir)
        self.subDirEntry.grid(row=row, column=3, stick='w')

        row += 1
        self.localVerLabel = Label(guiParent,
                                   text='Local version: %s' % self.version)
        self.localVerLabel.grid(row=row, column=0, sticky='w')

        self.serverLabel = Label(guiParent,
                                 text='Server version: %s' % version)
        self.serverLabel.grid(row=row, column=2, sticky='w')

        row += 1
        guiParent.grid_rowconfigure(row, weight=1)
        headingList = [
            'File',
            'Location',
            'Date',
            'Priority',
            'Comments',
            'StoredAs',
        ]

        editWidgets = [
            None, None, None, self.priorityEntry, self.commentsEntry
        ]
        editGetCallbacks = [
            None, None, None, self.getPriority, self.getComments
        ]
        editSetCallbacks = [
            None, None, None, self.setPriority, self.setComments
        ]
        self.scrolledMatrix = ScrolledMatrix(guiParent,
                                             headingList=headingList,
                                             multiSelect=True,
                                             editWidgets=editWidgets,
                                             callback=self.selectCell,
                                             editGetCallbacks=editGetCallbacks,
                                             editSetCallbacks=editSetCallbacks)
        self.scrolledMatrix.grid(row=row,
                                 column=0,
                                 columnspan=4,
                                 sticky='nsew')

        row += 1
        texts = [
            'Add\nFiles', 'Remove\nFiles', 'Remove\nAll', 'Query\nServer',
            'Commit\nSelected', 'Synchronise\nAll', 'Commit\nNew', 'Quit'
        ]
        commands = [
            self.addFile, self.removeFile, self.removeAll, self.queryFiles,
            self.synchroniseSelected, self.synchroniseServer,
            self.updateServer, self.quit
        ]
        self.buttonList = ButtonList(guiParent,
                                     texts=texts,
                                     commands=commands,
                                     expands=1)
        self.buttonList.grid(row=row, column=0, columnspan=4, sticky='ew')

        self.update()

    def getPriority(self, fileUpdate):

        if fileUpdate:
            self.priorityEntry.set(fileUpdate.priority)

    def setPriority(self, event):

        i = self.priorityEntry.get()
        if self.fileUpdate:
            self.fileUpdate.priority = i

        self.update()

    def getComments(self, fileUpdate):

        if fileUpdate:
            self.commentsEntry.set(fileUpdate.details)

    def setComments(self, event):

        text = self.commentsEntry.get()
        if self.fileUpdate:
            self.fileUpdate.details = text

        self.update()

    def quit(self):

        self.close()
        self.destroy()
        sys.exit()

    def updateServer(self):

        if self.server:
            # Unly upload new updates
            success = self.server.setFileUpdates()
            self.serverLabel.set('Server version: %s' % self.server.version)
            if success:
                self.queryFiles()

    def synchroniseSelected(self):

        if self.server:
            selectedUpdates = self.scrolledMatrix.currentObjects
            if selectedUpdates:
                # Refresh all selected updates
                success = self.server.setFileUpdates(
                    fileUpdates=selectedUpdates, refresh=True)
                self.serverLabel.set('Server version: %s' %
                                     self.server.version)
                if success:
                    self.queryFiles()

    def synchroniseServer(self):

        if self.server:
            # Refresh all avaiable updates
            success = self.server.setFileUpdates(refresh=True)
            self.serverLabel.set('Server version: %s' % self.server.version)
            if success:
                self.queryFiles()

    def queryFiles(self):

        if self.server:
            self.server.getFileUpdates()

        self.update()

    def addFile(self):

        if self.server:
            fileSelectPopup = FileSelectPopup(self,
                                              title='Select Source File',
                                              dismiss_text='Cancel',
                                              file_types=self.fileTypes,
                                              multiSelect=True,
                                              selected_file_must_exist=True)

            filePaths = fileSelectPopup.file_select.getFiles()
            n = len(self.installRoot)

            for filePath in filePaths:
                if self.installRoot != filePath[:n]:
                    showWarning(
                        'Warning',
                        'Install root %s not found in file path %s' %
                        (self.installRoot, filePath))
                    continue

                filePath = filePath[n + 1:]

                if filePath:
                    dirName, fileName = splitPath(filePath)

                    if fileName[-3:] == '.py':
                        language = 'python'
                    else:
                        language = 'None'

                    fileUpdate = FileUpdate(self.server,
                                            fileName,
                                            dirName,
                                            language,
                                            isNew=True)

            self.update()

    def removeFile(self):

        if self.fileUpdate:
            self.fileUpdate.delete()

            self.update()

    def removeAll(self):

        if self.server:
            for fileUpdate in list(self.server.fileUpdates):
                fileUpdate.delete()

            self.update()

    def selectAll(self):

        if self.server:
            for fileUpdate in self.server.fileUpdates:
                fileUpdate.isSelected = 1

        self.update()

    def selectCell(self, object, row, col):

        self.fileUpdate = object
        self.updateButtons()

    def updateButtons(self):

        buttons = self.buttonList.buttons

        if self.server:
            buttons[0].enable()
            buttons[3].enable()
            buttons[4].enable()
        else:
            buttons[0].disable()
            buttons[3].disable()
            buttons[4].disable()

        if self.server and self.server.fileUpdates:
            buttons[2].enable()
        else:
            buttons[2].disable()

        if self.fileUpdate:
            buttons[1].enable()
        else:
            buttons[1].disable()

    def update(self):

        location = self.serverEntry.get()
        uid = self.uidEntry.get()
        httpDir = self.httpDirEntry.get()
        subDir = self.subDirEntry.get()
        if self.server:
            if (location, uid, httpDir, subDir) != self.server.identity:
                self.setServer(location)

        self.updateButtons()

        self.fileUpdate = None
        textMatrix = []
        objectList = []
        colorMatrix = []

        if self.server:

            for fileUpdate in self.server.fileUpdates:

                datum = []
                datum.append(fileUpdate.fileName)
                datum.append(fileUpdate.filePath)
                datum.append(fileUpdate.date)
                datum.append(fileUpdate.priority)
                datum.append(fileUpdate.details)
                datum.append(fileUpdate.storedAs)

                textMatrix.append(datum)
                objectList.append(fileUpdate)
                if fileUpdate.isNew:
                    colorMatrix.append(5 * ['#B0FFB0'])

                elif not fileUpdate.getIsUpToDate():
                    colorMatrix.append(5 * ['#FFB0B0'])

                else:
                    colorMatrix.append(5 * [None])

        self.scrolledMatrix.update(textMatrix=textMatrix,
                                   objectList=objectList,
                                   colorMatrix=colorMatrix)
Exemple #12
0
class HcloudsMdPopup(BasePopup):
    def __init__(self, parent, *args, **kw):

        self.guiParent = parent
        self.project = parent.getProject()
        self.waiting = 0

        self.constraintSet = None
        self.constrLists = [None] * 4
        self.numClouds = 100
        self.filePrefix = 't_intra_'
        self.cloudsFiles = []
        self.adcAtomTypes = 'HN'

        # step num, initial temp, final temp, cooling steps, MD steps, MD tau, rep scale
        self.coolingScheme = []
        self.coolingScheme.append([1, 1, 1, 3, 500, 0.001, 0])
        self.coolingScheme.append([2, 80000, 4000, 19, 1000, 0.001, 0])
        self.coolingScheme.append([3, 4000, 1, 5, 500, 0.001, 0])
        self.coolingScheme.append([4, 15000, 1, 3, 1000, 0.001, 0])
        self.coolingScheme.append([5, 1, 1, 5, 500, 0.001, 0])
        self.coolingScheme.append([6, 8000, 1, 3, 1000, 0.001, 0])
        self.coolingScheme.append([7, 1, 1, 5, 500, 0.001, 0])
        self.coolingScheme.append([8, 3000, 25, 60, 2500, 0.001, 1])
        self.coolingScheme.append([9, 25, 25, 1, 7500, 0.001, 1])
        self.coolingScheme.append([10, 10, 10, 1, 7500, 0.001, 1])
        self.coolingScheme.append([11, 0.01, 0.01, 1, 7500, 0.0005, 1])

        self.coolingStep = None

        BasePopup.__init__(self,
                           parent,
                           title="Hydrogen Cloud Molecular Dynamics",
                           **kw)

    def body(self, guiFrame):

        self.mdInitTempEntry = FloatEntry(self,
                                          text='',
                                          returnCallback=self.setMdInitTemp)
        self.mdFinTempEntry = FloatEntry(self,
                                         text='',
                                         returnCallback=self.setMdFinTemp)
        self.mdCoolStepsEntry = IntEntry(self,
                                         text='',
                                         returnCallback=self.setMdCoolSteps)
        self.mdSimStepsEntry = IntEntry(self,
                                        text='',
                                        returnCallback=self.setMdSimSteps)
        self.mdTauEntry = FloatEntry(self,
                                     text='',
                                     returnCallback=self.setMdTau)
        self.mdRepScaleEntry = FloatEntry(self,
                                          text='',
                                          returnCallback=self.setMdRepScale)

        guiFrame.grid_columnconfigure(0, weight=1)

        row = 0
        guiFrame.grid_rowconfigure(row, weight=1)
        frame = LabelFrame(guiFrame, text='Input constraints')
        frame.grid(row=row, column=0, sticky=Tkinter.NSEW)
        frame.grid_columnconfigure(2, weight=1)

        srow = 0
        label = Label(frame, text='Constraint set:')
        label.grid(row=srow, column=0, sticky=Tkinter.W)
        self.constraintSetPulldown = PulldownMenu(
            frame,
            callback=self.changeConstraintSet,
            selected_index=0,
            do_initial_callback=0)
        self.constraintSetPulldown.grid(row=srow, column=1, sticky=Tkinter.W)

        srow += 1
        label = Label(frame, text='Dist constraint list 1:')
        label.grid(row=srow, column=0, sticky=Tkinter.W)
        self.distance1Pulldown = PulldownMenu(
            frame,
            callback=self.changeDistance1ConstraintList,
            selected_index=0,
            do_initial_callback=0)
        self.distance1Pulldown.grid(row=srow, column=1, sticky=Tkinter.W)
        self.numConstr1Label = Label(frame, text='Constraints: 0')
        self.numConstr1Label.grid(row=srow, column=2, sticky=Tkinter.W)

        srow += 1
        label = Label(frame, text='Dist constraint list 2:')
        label.grid(row=srow, column=0, sticky=Tkinter.W)
        self.distance2Pulldown = PulldownMenu(
            frame,
            callback=self.changeDistance2ConstraintList,
            selected_index=0,
            do_initial_callback=0)
        self.distance2Pulldown.grid(row=srow, column=1, sticky=Tkinter.W)
        self.numConstr2Label = Label(frame, text='Constraints: 0')
        self.numConstr2Label.grid(row=srow, column=2, sticky=Tkinter.W)

        srow += 1
        label = Label(frame, text='Dist constraint list 3:')
        label.grid(row=srow, column=0, sticky=Tkinter.W)
        self.distance3Pulldown = PulldownMenu(
            frame,
            callback=self.changeDistance3ConstraintList,
            selected_index=0,
            do_initial_callback=0)
        self.distance3Pulldown.grid(row=srow, column=1, sticky=Tkinter.W)
        self.numConstr3Label = Label(frame, text='Constraints: 0')
        self.numConstr3Label.grid(row=srow, column=2, sticky=Tkinter.W)

        srow += 1
        label = Label(frame, text='Dist constraint list 4:')
        label.grid(row=srow, column=0, sticky=Tkinter.W)
        self.distance4Pulldown = PulldownMenu(
            frame,
            callback=self.changeDistance4ConstraintList,
            selected_index=0,
            do_initial_callback=0)
        self.distance4Pulldown.grid(row=srow, column=1, sticky=Tkinter.W)
        self.numConstr4Label = Label(frame, text='Constraints: 0')
        self.numConstr4Label.grid(row=srow, column=2, sticky=Tkinter.W)

        row += 1
        frame0 = LabelFrame(guiFrame, text='Cooling scheme')
        frame0.grid(row=row, column=0, sticky=Tkinter.NSEW)
        frame0.grid_columnconfigure(1, weight=1)

        f0row = 0
        frame0.grid_rowconfigure(f0row, weight=1)
        colHeadings = [
            'Step', 'Initial\nTemp.', 'Final\nTemp.', 'Cooling\nSteps',
            'MD Steps', 'MD Tau', 'Rep.\nScale'
        ]
        editWidgets = [
            None, self.mdInitTempEntry, self.mdFinTempEntry,
            self.mdCoolStepsEntry, self.mdSimStepsEntry, self.mdTauEntry,
            self.mdRepScaleEntry
        ]
        editGetCallbacks = [
            None, self.getMdInitTemp, self.getMdFinTemp, self.getMdCoolSteps,
            self.getMdSimSteps, self.getMdTau, self.getMdRepScale
        ]
        editSetCallbacks = [
            None, self.setMdInitTemp, self.setMdFinTemp, self.setMdCoolSteps,
            self.setMdSimSteps, self.setMdTau, self.setMdRepScale
        ]
        self.coolingSchemeMatrix = ScrolledMatrix(
            frame0,
            editSetCallbacks=editSetCallbacks,
            editGetCallbacks=editGetCallbacks,
            editWidgets=editWidgets,
            maxRows=9,
            initialRows=12,
            headingList=colHeadings,
            callback=self.selectCoolingStep,
            objectList=self.coolingScheme,
            textMatrix=self.coolingScheme)
        self.coolingSchemeMatrix.grid(row=f0row,
                                      column=0,
                                      columnspan=4,
                                      sticky=Tkinter.NSEW)

        f0row += 1
        texts = ['Move earlier', 'Move later', 'Add step', 'Remove step']
        commands = [
            self.moveStepEarlier, self.moveStepLater, self.addCoolingStep,
            self.removeCoolingStep
        ]
        self.coolingSchemeButtons = ButtonList(frame0,
                                               expands=1,
                                               commands=commands,
                                               texts=texts)
        self.coolingSchemeButtons.grid(row=f0row,
                                       column=0,
                                       columnspan=4,
                                       sticky=Tkinter.EW)

        row += 1
        guiFrame.grid_rowconfigure(row, weight=1)
        frame1 = LabelFrame(guiFrame, text='Dynamics control')
        frame1.grid(row=row, column=0, sticky=Tkinter.NSEW)
        frame1.grid_columnconfigure(1, weight=1)

        f1row = 0
        label20 = Label(frame1, text='Number of clouds:')
        label20.grid(row=f1row, column=0, sticky=Tkinter.NW)
        self.numCloudsEntry = IntEntry(frame1,
                                       text=1,
                                       returnCallback=self.setNumClouds,
                                       width=10)
        self.numCloudsEntry.grid(row=f1row, column=1, sticky=Tkinter.NW)
        label21 = Label(frame1, text='Cloud file prefix:')
        label21.grid(row=f1row, column=2, sticky=Tkinter.NW)
        self.filePrefixEntry = Entry(frame1,
                                     text='t_intra_',
                                     returnCallback=self.setFilePrefix,
                                     width=10)
        self.filePrefixEntry.grid(row=f1row, column=3, sticky=Tkinter.NW)

        f1row += 1
        texts = ['Start molecular dynamics', 'Show dynamics progress']
        commands = [self.startMd, self.showMdProgress]
        self.mdButtons = ButtonList(frame1,
                                    expands=1,
                                    commands=commands,
                                    texts=texts)
        self.mdButtons.grid(row=f1row,
                            column=0,
                            columnspan=4,
                            sticky=Tkinter.NSEW)

        row += 1
        self.bottomButtons = createDismissHelpButtonList(guiFrame,
                                                         expands=0,
                                                         help_url=None)
        self.bottomButtons.grid(row=row, column=0, sticky=Tkinter.EW)

        self.update()

        for func in ('__init__', 'delete', 'setName'):
            for clazz in ('ccp.nmr.NmrConstraint.DistanceConstraintList', ):
                Implementation.registerNotify(self.updateConstraintLists,
                                              clazz, func)
        for func in ('__init__', 'delete'):
            Implementation.registerNotify(
                self.updateConstraintSets,
                'ccp.nmr.NmrConstraint.NmrConstraintStore', func)

    def getContraintSetNames(self):

        names = []
        constraintSets = self.project.currentNmrProject.nmrConstraintStores
        for set in constraintSets:
            names.append('%d' % set.serial)

        return names

    def changeConstraintSet(self, i, name):

        project = self.project
        if project.currentNmrProject.nmrConstraintStores:
            constraintSet = project.currentNmrProject.sortedNmrConstraintStores(
            )[i]
        else:
            constraintSet = None

        if constraintSet is not self.constraintSet:
            self.constraintSet = constraintSet

        self.updateConstraintLists()
        self.update()

    def updateConstraintLists(self, *opt):

        constrListData = self.getConstraintLists()
        constrListNames = [x[0] for x in constrListData]
        constraintLists = [x[1] for x in constrListData]
        # copes with self.constraintSet being None

        if constrListNames:
            i = 0
            for constraintList in self.constrLists:
                if constraintList not in constraintLists:
                    if i == 0:
                        self.constrLists[i] = constraintLists[0]
                    else:
                        self.constrLists[i] = None
                i += 1

            constraintLists.append(None)
            constrListNames.append('<None>')
            self.distance1Pulldown.setup(
                constrListNames, constraintLists.index(self.constrLists[0]))
            self.distance2Pulldown.setup(
                constrListNames, constraintLists.index(self.constrLists[1]))
            self.distance3Pulldown.setup(
                constrListNames, constraintLists.index(self.constrLists[2]))
            self.distance4Pulldown.setup(
                constrListNames, constraintLists.index(self.constrLists[3]))

        else:
            self.constrLists = [None] * 4
            self.distance1Pulldown.setup([], -1)
            self.distance2Pulldown.setup([], -1)
            self.distance3Pulldown.setup([], -1)
            self.distance4Pulldown.setup([], -1)

    def updateConstraintSets(self, *opt):

        project = self.project
        constraintSets = list(project.currentNmrProject.nmrConstraintStores)
        if constraintSets:
            constraintSetNames = self.getContraintSetNames()

            # set defaults
            if self.constraintSet not in constraintSets:
                self.constraintSet = constraintSets[0]

            if self.constraintSet:
                j = 0
                for constraintList in self.constrLists:
                    if constraintList and (constraintList.nmrConstraintStore
                                           is not self.constraintSet):
                        if self.constraintSet.constraintLists and j == 0:
                            self.constrLists[
                                j] = self.constraintSet.constraintLists[0]
                        else:
                            self.constrLists[j] = None
                    j += 1

            else:
                self.constrLists = [None] * 4

            i = constraintSets.index(self.constraintSet)
            self.constraintSetPulldown.setup(constraintSetNames, i)

        else:
            self.constraintSet = None
            self.constrLists = [None] * 4
            self.constraintSetPulldown.setup([], -1)

    def getConstraintListName(self, constraintList):

        if constraintList.name:
            listName = ':%s' % (constraintList.name)
        else:
            listName = ''

        name = '%d:%d:%s%s' % (constraintList.nmrConstraintStore.serial,
                               constraintList.serial,
                               constraintList.className[:-14], listName)
        return name

    def getConstraintLists(self):

        constraintLists = []
        if self.constraintSet:
            for constraintList in self.constraintSet.constraintLists:
                if constraintList.className == 'DistanceConstraintList':
                    name = self.getConstraintListName(constraintList)
                    constraintLists.append([name, constraintList])

        return constraintLists

    def changeDistance1ConstraintList(self, i, name):
        self.changeDistanceConstraintList(i, name, 0)

    def changeDistance2ConstraintList(self, i, name):
        self.changeDistanceConstraintList(i, name, 1)

    def changeDistance3ConstraintList(self, i, name):
        self.changeDistanceConstraintList(i, name, 2)

    def changeDistance4ConstraintList(self, i, name):
        self.changeDistanceConstraintList(i, name, 3)

    def changeDistanceConstraintList(self, i, name, listNum):

        project = self.project
        constraintLists = self.getConstraintLists()

        if constraintLists and (i < 4):
            self.constrLists[listNum] = constraintLists[i][1]
        else:
            self.constrLists[listNum] = None

        self.update()

    def startMd(self):

        self.setNumClouds()
        self.setFilePrefix()
        if ((self.constrLists != [None] * 4) and (self.numClouds > 0)
                and self.filePrefix):

            resDict = {}
            for resonance in self.guiParent.project.currentNmrProject.resonances:
                resDict[resonance.serial] = resonance

            constraints = []

            constraintStore = None
            for dcl in self.constrLists:
                if dcl:
                    constraintStore = dcl.nmrConstraintStore
                    constraints.extend(list(dcl.constraints))

            resonances = []
            for constraint in constraints:
                for item in constraint.items:
                    for fixedResonance in item.resonances:
                        if fixedResonance.resonanceSerial is None:
                            resonance = newResonance(
                                self.guiParent.project,
                                isotopeCode=fixedResonance.isotopeCode)
                            resonance.setName(fixedResonance.name)
                            fixedResonance.setResonanceSerial(resonance.serial)
                            resDict[resonance.serial] = resonance
                            if fixedResonance.resonanceSet:
                                atomSets = list(
                                    fixedResonance.resonanceSet.atomSets)
                                assignAtomsToRes(atomSets, resonance)

                        if resDict.get(
                                fixedResonance.resonanceSerial) is not None:
                            resonances.append(
                                resDict[fixedResonance.resonanceSerial])
                            resDict[fixedResonance.resonanceSerial] = None

            resonances, intraConstraintList = self.makeIntraConstraints(
                resonances, constraintStore)
            constraints.extend(list(intraConstraintList.constraints))
            resonances, interConstraintList = self.makeInterConstraints(
                resonances, constraintStore)
            constraints.extend(list(interConstraintList.constraints))

            startMdProcess(self.numClouds, constraints, resonances,
                           self.coolingScheme, self.filePrefix)

            #structGen = self.distanceConstraintList.structureGeneration

            serials = []
            for resonance in resonances:
                serials.append(resonance.serial)
            clouds = []
            for i in range(self.numClouds):
                clouds.append('%s%3.3d.pdb' % (self.filePrefix, i))
            self.guiParent.application.setValues(constraintStore,
                                                 'clouds',
                                                 values=clouds)
            self.guiParent.application.setValues(constraintStore,
                                                 'cloudsResonances',
                                                 values=serials)

            # do better than this check for creation

    def makeInterConstraints(self, resonances, constraintSet):

        from ccpnmr.analysis.core.ConstraintBasic import getFixedResonance
        from ccpnmr.analysis.core.AssignmentBasic import findConnectedSpinSystem

        project = constraintSet.root
        constraintList = constraintSet.newDistanceConstraintList()
        constraintList.name = 'Seq connections'

        resDict = {}
        spinSystemDict = {}
        for resonance in resonances:
            resDict[resonance] = True
            spinSystem = resonance.resonanceGroup
            if spinSystem:
                spinSystemDict[spinSystem] = None

        spinSystems = spinSystemDict.keys()

        for spinSystem in spinSystems:
            nextSpinSystem = findConnectedSpinSystem(spinSystem, delta=1)
            if nextSpinSystem:
                ca = spinSystem.newAtoms.get('CA')
                c = spinSystem.newAtoms.get('C')
                n = nextSpinSystem.newAtoms.get('N')

                if ca and c and n:
                    if resDict.get(ca) is None:
                        resonances.append(ca)
                    if resDict.get(c) is None:
                        resonances.append(c)
                    if resDict.get(n) is None:
                        resonances.append(n)

                    c_n = constraintList.newDistanceConstraint(
                        weight=1.0,
                        origData=1.0,
                        targetValue=1.32,
                        upperLimit=1.35,
                        lowerLimit=1.29,
                        error=0.06)

                    # below based on angle constraints
                    ca_n = constraintList.newDistanceConstraint(
                        weight=1.0,
                        origData=1.0,
                        targetValue=2.415,
                        upperLimit=2.513,
                        lowerLimit=2.316,
                        error=0.197)

                    frCa = getFixedResonance(constraintSet, ca)
                    frC = getFixedResonance(constraintSet, c)
                    frN = getFixedResonance(constraintSet, n)

                    item = ca_n.newDistanceConstraintItem(
                        resonances=[frCa, frN])
                    item = c_n.newDistanceConstraintItem(resonances=[frC, frN])

        return resonances, constraintList

    def makeIntraConstraints(self, resonances, constraintSet):

        from ccpnmr.analysis.core.ConstraintBasic import getFixedResonance

        project = constraintSet.root
        constraintList = constraintSet.newDistanceConstraintList()
        constraintList.name = 'Backbone intra'

        dict = {}
        for resonance in resonances:
            if resonance.resonanceGroup and resonance.assignNames:
                dict[resonance] = 1
                resonance.resonanceGroup.newAtoms = {}

        for resonance in dict.keys():
            ss = resonance.resonanceGroup
            if resonance.assignNames[0] == 'H':
                for resonance2 in ss.resonances:
                    if dict.get(resonance2
                                ) and resonance2.assignNames[0][:2] == 'HA':

                        ca = ss.newAtoms.get('CA')
                        if ca is None:
                            ca = project.newResonance(isotopeCode='13C',
                                                      assignNames=[
                                                          'CA',
                                                      ])
                            resonances.append(ca)

                        c = ss.newAtoms.get('C')
                        if c is None:
                            c = project.newResonance(isotopeCode='13C',
                                                     assignNames=[
                                                         'C',
                                                     ])
                            resonances.append(c)

                        n = ss.newAtoms.get('N')
                        if n is None:
                            n = project.newResonance(isotopeCode='15N',
                                                     assignNames=[
                                                         'N',
                                                     ])
                            resonances.append(n)

                        ss.newAtoms['C'] = c
                        ss.newAtoms['CA'] = ca
                        ss.newAtoms['N'] = n

                        frCa = getFixedResonance(constraintSet, ca)
                        frC = getFixedResonance(constraintSet, c)
                        frN = getFixedResonance(constraintSet, n)
                        frH = getFixedResonance(constraintSet, resonance)
                        frha = getFixedResonance(constraintSet, resonance2)

                        h_n = constraintList.newDistanceConstraint(
                            weight=1.0,
                            origData=1.0,
                            targetValue=1.01,
                            upperLimit=1.1,
                            lowerLimit=0.9,
                            error=0.2)
                        n_ca = constraintList.newDistanceConstraint(
                            weight=1.0,
                            origData=1.0,
                            targetValue=1.465,
                            upperLimit=1.50,
                            lowerLimit=1.43,
                            error=0.07)
                        ca_c = constraintList.newDistanceConstraint(
                            weight=1.0,
                            origData=1.0,
                            targetValue=1.525,
                            upperLimit=1.55,
                            lowerLimit=1.50,
                            error=0.05)

                        # below based on angle constraints
                        n_c = constraintList.newDistanceConstraint(
                            weight=1.0,
                            origData=1.0,
                            targetValue=2.464,
                            upperLimit=2.572,
                            lowerLimit=2.356,
                            error=0.216)

                        item = h_n.newDistanceConstraintItem(
                            resonances=[frH, frN])
                        item = n_ca.newDistanceConstraintItem(
                            resonances=[frN, frCa])
                        item = ca_c.newDistanceConstraintItem(
                            resonances=[frCa, frC])
                        item = n_c.newDistanceConstraintItem(
                            resonances=[frN, frC])

                        if ss.newAtoms.get('CAHA') is None:
                            ca_ha = constraintList.newDistanceConstraint(
                                weight=1.0,
                                origData=1.0,
                                targetValue=1.09,
                                upperLimit=1.19,
                                lowerLimit=0.99,
                                error=0.2)
                            item = ca_ha.newDistanceConstraintItem(
                                resonances=[frC, frha])
                            ss.newAtoms['CAHA'] = 1

            if resonance.assignNames[0][:2] == 'HA':
                for resonance2 in ss.resonances:
                    if dict.get(resonance2
                                ) and resonance2.assignNames[0][:2] == 'HB':

                        ca = ss.newAtoms.get('CA')
                        if ca is None:
                            ca = project.newResonance(isotopeCode='13C',
                                                      assignNames=[
                                                          'CA',
                                                      ])
                            resonances.append(ca)

                        cb = ss.newAtoms.get('CB')
                        if cb is None:
                            cb = project.newResonance(isotopeCode='13C',
                                                      assignNames=[
                                                          'CB',
                                                      ])
                            resonances.append(cb)

                        ss.newAtoms['CA'] = cb
                        ss.newAtoms['CB'] = ca
                        ss.newAtoms['CAHA'] = 1

                        frCA = getFixedResonance(constraintSet, ca)
                        frCB = getFixedResonance(constraintSet, cb)
                        frHA = getFixedResonance(constraintSet, resonance)
                        frHB = getFixedResonance(constraintSet, resonance2)

                        c_b = constraintList.newDistanceConstraint(
                            weight=1.0,
                            origData=1.0,
                            targetValue=1.09,
                            upperLimit=1.19,
                            lowerLimit=0.99,
                            error=0.2)
                        c_c = constraintList.newDistanceConstraint(
                            weight=1.0,
                            origData=1.0,
                            targetValue=1.53,
                            upperLimit=1.56,
                            lowerLimit=1.51,
                            error=0.05)

                        item = c_b.newDistanceConstraintItem(
                            resonances=[frCB, frHB])
                        item = c_c.newDistanceConstraintItem(
                            resonances=[frCA, frCB])

                        if ss.newAtoms.get('CAHA') is None:
                            c_a = constraintList.newDistanceConstraint(
                                weight=1.0,
                                origData=1.0,
                                targetValue=1.09,
                                upperLimit=1.19,
                                lowerLimit=0.99,
                                error=0.2)
                            item = c_a.newDistanceConstraintItem(
                                resonances=[frCA, frHA])
                            ss.newAtoms['CAHA'] = 1

        return resonances, constraintList

    def showMdProgress(self):

        n = 0
        m = self.numClouds
        for i in range(m):
            pdbFileName = '%s%3.3d.pdb' % (self.filePrefix, i)
            if os.path.exists(pdbFileName):
                n += 1

        p = n * 100 / float(m)
        text = 'Done %d of %d clouds (%1.2f)%%' % (n, m, p)
        showInfo('MD Progress', text)

    def setFilePrefix(self, text=None):

        if not text:
            text = self.filePrefixEntry.get()

        if text:
            self.filePrefix = text

    def setNumClouds(self, n=None, *event):

        if not n:
            n = self.numCloudsEntry.get()

        if n:
            self.numClouds = int(n)

    def update(self):

        self.updateConstraintSets()
        self.updateConstraintLists()

        if (self.constrLists != [None] * 4) and self.coolingScheme:
            self.mdButtons.buttons[0].enable()
            self.mdButtons.buttons[1].enable()
        else:
            self.mdButtons.buttons[0].disable()
            self.mdButtons.buttons[1].disable()

        if self.constrLists[0]:
            self.numConstr1Label.set('Constraints: ' +
                                     str(len(self.constrLists[0].constraints)))
        else:
            self.numConstr1Label.set('Constraints: 0')

        if self.constrLists[1]:
            self.numConstr2Label.set('Constraints: ' +
                                     str(len(self.constrLists[1].constraints)))
        else:
            self.numConstr2Label.set('Constraints: 0')

        if self.constrLists[2]:
            self.numConstr3Label.set('Constraints: ' +
                                     str(len(self.constrLists[2].constraints)))
        else:
            self.numConstr3Label.set('Constraints: 0')

        if self.constrLists[3]:
            self.numConstr4Label.set('Constraints: ' +
                                     str(len(self.constrLists[3].constraints)))
        else:
            self.numConstr4Label.set('Constraints: 0')

    def getMdInitTemp(self, coolingStep):

        self.mdInitTempEntry.set(coolingStep[1])

    def getMdFinTemp(self, coolingStep):

        self.mdFinTempEntry.set(coolingStep[2])

    def getMdCoolSteps(self, coolingStep):

        self.mdCoolStepsEntry.set(coolingStep[3])

    def getMdSimSteps(self, coolingStep):

        self.mdSimStepsEntry.set(coolingStep[4])

    def getMdTau(self, coolingStep):

        self.mdTauEntry.set(coolingStep[5])

    def getMdRepScale(self, coolingStep):

        self.mdRepScaleEntry.set(coolingStep[6])

    def setMdInitTemp(self, event):

        value = self.mdInitTempEntry.get()
        if value is not None:
            self.coolingStep[1] = value

        self.updateCoolingScheme()

    def setMdFinTemp(self, event):

        value = self.mdFinTempEntry.get()
        if value is not None:
            self.coolingStep[2] = value

        self.updateCoolingScheme()

    def setMdCoolSteps(self, event):

        value = self.mdCoolStepsEntry.get()
        if value is not None:
            self.coolingStep[3] = value

        self.updateCoolingScheme()

    def setMdSimSteps(self, event):

        value = self.mdSimStepsEntry.get()
        if value is not None:
            self.coolingStep[4] = value

        self.updateCoolingScheme()

    def setMdTau(self, event):

        value = self.mdTauEntry.get()
        if value is not None:
            self.coolingStep[5] = value

        self.updateCoolingScheme()

    def setMdRepScale(self, event):

        value = self.mdRepScaleEntry.get()
        if value is not None:
            self.coolingStep[6] = value

        self.updateCoolingScheme()

    def selectCoolingStep(self, object, row, col):

        self.coolingStep = object

    def moveStepEarlier(self):

        if self.coolingStep:
            i = self.coolingStep[0] - 1
            if i > 0:
                coolingStep = self.coolingScheme[i - 1]
                coolingStep[0] = i + 1
                self.coolingStep[0] = i
                self.coolingScheme[i - 1] = self.coolingStep
                self.coolingScheme[i] = coolingStep

                self.updateCoolingScheme()
                self.coolingSchemeMatrix.hilightObject(self.coolingStep)

    def moveStepLater(self):

        if self.coolingStep:
            i = self.coolingStep[0] - 1
            if i < len(self.coolingScheme) - 1:
                coolingStep = self.coolingScheme[i + 1]
                coolingStep[0] = i + 1
                self.coolingStep[0] = i + 2
                self.coolingScheme[i + 1] = self.coolingStep
                self.coolingScheme[i] = coolingStep

                self.updateCoolingScheme()
                self.coolingSchemeMatrix.hilightObject(self.coolingStep)

    def addCoolingStep(self):

        i = len(self.coolingScheme) + 1
        datum = [i, 3000, 100, 10, 2500, 0.001, 1]

        self.coolingScheme.append(datum)
        self.updateCoolingScheme()

    def removeCoolingStep(self):

        if self.coolingStep:
            coolingScheme = []
            i = 0
            for coolingStep in self.coolingScheme:
                if coolingStep is not self.coolingStep:
                    i += 1
                    coolingStep[0] = i
                    coolingScheme.append(coolingStep)

            self.coolingScheme = coolingScheme
            self.updateCoolingScheme()

    def updateCoolingScheme(self):

        objectList = self.coolingScheme
        textMatrix = self.coolingScheme
        self.coolingSchemeMatrix.update(objectList=objectList,
                                        textMatrix=textMatrix)

    def updateMidgeParams(self):

        data = [
            self.specFreq, self.maxIter, self.mixTime, self.corrTime,
            self.leakRate, self.maxIntens
        ]

        self.midgeParamsMatrix.update(textMatrix=[
            data,
        ])

    def destroy(self):

        for func in ('__init__', 'delete', 'setName'):
            for clazz in ('ccp.nmr.NmrConstraint.DistanceConstraintList', ):
                Implementation.unregisterNotify(self.updateConstraintLists,
                                                clazz, func)
        for func in ('__init__', 'delete'):
            Implementation.unregisterNotify(
                self.updateConstraintSets,
                'ccp.nmr.NmrConstraint.NmrConstraintStore', func)

        BasePopup.destroy(self)