Esempio n. 1
0
class ResidueInfoPopup(BasePopup):
    """
  **Residue Assignment Information**
  
  This popup is designed to give an alternative view of residue assignment
  status to the `Atom Browser`_. The idea is that the user selects a particular
  molecular chain from the upper-left pulldown menu and then a *type* of residue
  from that chain in the adjacent pulldown. The left hand table then shows all
  the residues of that type within the selected chain's sequence.

  The left hand table lists residues of the selected type in the middle column.
  The first and last columns show the previous and next residue in the sequence
  for each central residue, thus showing a little of the sequence context for
  the selected kind of residue. If a row of this table is clicked, the central
  "i" residue is selected and the assignments are displayed in a 3D structural
  view in the right hand panel.

  The right hand panel contains a structural display for the kind of residue
  selected, using *idealised* coordinates; not coordinates from a particular
  structure. With the "Show Assignment" option selected, this 3D view has the
  chemical shifts of the assigned atoms within the selected residue superimposed
  in the view. The chemical shifts are listed after the name of each atom, but
  naturally only if it has a resonance assignment in the selected shift list.

  It should be noted that where atoms are deemed to be equivalent, like the
  three hydrogens in a fast rotating methyl atom set, the same chemical shift
  values will be used for all of the atoms within the set, although strictly
  speaking the value only really applies to the set as a whole. Also, for
  assignments that are non-stereospecific, e.g. there may be an assignment to
  Ser HBa which doesn't commit to either HB2 or HB3 specifically, the display
  will show both possible chemical shift values (should they exist) for a given
  atom. For example, a Ser HB2 atom may be labeled as "HB" 3.72/3.85" because it
  potentially relates to either the "HBa" resonance at 3.72 ppm or the "HBb"
  resonance at 3.85 ppm.

  **3D View Controls**
  
  To move and rotate the three-dimensional residue display the following
  keyboard controls may be used:
  
  * Rotate: Arrow keys
  
  * Zoom: Page Up & Page Down keys

  * Translate: Arrow keys + Control key

  Or altenatively the following mouse controls:
  
  * Rotate: Middle button click & drag
  
  * Zoom: Mouse wheel or middle button click + Shift key & drag up/down

  * Translate: Middle button click & drag + Control key

  _`Atom Browser`: BrowseAtomsPopup.html
  
  """
    def __init__(self, parent, *args, **kw):

        self.residue = None
        self.chain = None
        self.shiftList = None

        BasePopup.__init__(self,
                           parent=parent,
                           title="Molecule : Residue Information",
                           **kw)

    def open(self):

        self.updateChains()
        BasePopup.open(self)

    def body(self, guiFrame):

        self.refresh = False
        self.hilightColor = 'lightBlue'
        self.matrixCellGrey = 'grey82'
        self.chain = None
        self.ccpCode = None

        row = 0
        label = Label(guiFrame, text='Molecule Chain:', grid=(row, 0))
        tipText = 'Selects which molecular chain to select and show residues from'
        self.chainPulldown = PulldownList(guiFrame,
                                          callback=self.changeChain,
                                          grid=(row, 1),
                                          tipText=tipText)

        label = Label(guiFrame, text='Ccp Residue Code:', grid=(row, 2))
        tipText = 'Selects which kind of residue to display sequence and assignment information for'
        self.ccpCodePulldown = PulldownList(guiFrame,
                                            callback=self.changeCcpCode,
                                            grid=(row, 3),
                                            tipText=tipText)

        label = Label(guiFrame, text='Shift List:', grid=(row, 4))
        tipText = 'Selects which shiftlist to get chemical shift values from'
        self.shiftListPulldown = PulldownList(guiFrame,
                                              callback=self.changeShiftList,
                                              grid=(row, 5),
                                              tipText=tipText)

        row += 1
        guiFrame.expandGrid(row, 6)

        self.residueFrame = LabelFrame(guiFrame,
                                       text='%s Information' % (self.ccpCode),
                                       grid=(row, 0),
                                       gridSpan=(1, 3))
        self.residueFrame.expandGrid(1, 0)

        self.atomsFrame = LabelFrame(
            guiFrame,
            text='Atom Information & Idealised Structure',
            grid=(row, 3),
            gridSpan=(1, 5))
        self.atomsFrame.expandGrid(0, 0)

        self.assignedText = 'Assigned:'
        tipText = 'How many residues of the selected kind are assigned out of the total number available'
        self.assignedLabel = Label(self.residueFrame,
                                   text=self.assignedText,
                                   grid=(0, 0),
                                   sticky='ew',
                                   tipText=tipText)

        tipTexts = [
            'Identity of the previous residue in the sequence',
            'Locations of the selected kind of residue, considering the selected molecular chain',
            'Identity of the next residue in the sequence'
        ]
        headingList = ['i-1', 'i', 'i+1']
        self.neighbourMatrix = ScrolledMatrix(self.residueFrame,
                                              initialRows=8,
                                              headingList=headingList,
                                              minCellWidth=6,
                                              sorting=0,
                                              highlightType=1,
                                              callback=self.selectResidue,
                                              grid=(1, 0),
                                              gridSpan=(1, 3),
                                              tipTexts=tipTexts)

        self.viewResidueFrame = ViewResidueFrame(self.atomsFrame,
                                                 residue=self.residue,
                                                 project=self.project,
                                                 grid=(0, 0),
                                                 tipText=tipText)

        tipTexts = [
            'Print the three-dimensional coordinate display to a PostScript, EPS or PDF file',
        ]
        texts = ['Print']
        commands = [self.viewResidueFrame.printStructure]
        self.utilButtons = UtilityButtonList(guiFrame,
                                             helpUrl=self.help_url,
                                             commands=commands,
                                             texts=texts,
                                             grid=(0, 7),
                                             tipTexts=tipTexts)

        self.updateChains()
        self.updateShiftLists()
        self.updateAfter()
        self.administerNotifiers(self.registerNotify)

    def administerNotifiers(self, notifyFunc):

        for func in ('__init__', 'delete'):
            for clazz in ('ccp.nmr.Nmr.ResonanceSet', ):
                notifyFunc(self.updateAfter, clazz, func)

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

        for func in ('__init__', 'delete', 'setName'):
            for clazz in ('ccp.nmr.Nmr.ShiftList', ):
                notifyFunc(self.updateShiftLists, clazz, func)

        notifyFunc(self.updateAfter, 'ccp.molecule.MolSystem.Residue',
                   'setSeqCode')

    def destroy(self):

        self.administerNotifiers(self.unregisterNotify)
        BasePopup.destroy(self)

    def selectResidue(self, residue, row, col):

        self.residue = residue
        self.updateAfter()

    def getChains(self, project):

        chains = []
        chainCodes = []
        for molSystem in self.project.sortedMolSystems():
            for chain in molSystem.sortedChains():
                chains.append(chain)
                chainCodes.append(self.getChainName(chain))

        return [chains, chainCodes]

    def getCcpCodes(self, chain):

        if not chain:
            return []

        dict = {}
        for residue in chain.residues:
            dict[residue.molResidue.ccpCode] = residue.molResidue.molType

        codes = []
        codesTemp = dict.keys()
        codesTemp.sort()
        for code in codesTemp:
            codes.append(code)

        return codes

    def getChainName(self, chain):

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

    def changeChain(self, chain):

        if self.chain is not chain:
            self.chain = chain
            self.updateCcpCodes()
            self.residue = None

    def changeCcpCode(self, ccpCode):

        if self.ccpCode != ccpCode:
            self.ccpCode = ccpCode
            self.residue = None
            self.updateAfter()

    def changeShiftList(self, shiftList):

        if shiftList is not self.shiftList:
            self.shiftList = shiftList
            self.updateAfter()

    def updateChains(self, *opt):

        index = []
        chain = self.chain
        [chains, chainCodes] = self.getChains(self.project)

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

            index = chains.index(chain)

        else:
            chain = None

        if self.chain is not chain:
            self.chain = chain
            self.updateCcpCodes()

        self.chainPulldown.setup(chainCodes, chains, index)

    def updateCcpCodes(self, *opt):

        index = 0
        ccpCodes = []

        if self.chain:
            ccpCodes = self.getCcpCodes(self.chain)

            if self.ccpCode not in ccpCodes:
                self.ccpCode = ccpCodes[0]
                self.residue = None

            index = ccpCodes.index(self.ccpCode)

            if self.residue and (self.residue.chain is not self.chain):
                self.residue = None

        self.ccpCodePulldown.setup(ccpCodes, ccpCodes, index)
        self.updateAfter()

    def updateShiftLists(self, *opt):

        index = 0
        names = []
        shiftLists = getShiftLists(self.nmrProject)
        shiftList = self.shiftList

        if shiftLists:
            if shiftList not in shiftLists:
                shiftList = shiftLists[0]

            names = ['%d:%s' % (sl.serial, sl.name) for sl in shiftLists]
            index = shiftLists.index(shiftList)

        else:
            shiftList = None

        if shiftList is not self.shiftList:
            self.changeShiftList(shiftList)

        self.shiftListPulldown.setup(names, shiftLists, index)

    def updateAfter(self, *opt):

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

    def update(self):

        # problem is that selectCell updates on old residue list
        # selecting a cell doesn't normally update the matrix
        if not self.chain:
            self.refresh = False
            return

        chain = self.chain
        ccpCode = self.ccpCode
        sameTypeResidues = []
        seqNeighbours = []
        neighbourData = []
        colorData = []
        assigned = 0

        residues = chain.sortedResidues()

        for residue in residues:
            if residue.molResidue.ccpCode == ccpCode:
                if self.residue is None:
                    self.residue = residue
                sameTypeResidues.append(residue)
                neighbourData.append([None, None, None])
                colorData.append([
                    self.matrixCellGrey, self.matrixCellGrey,
                    self.matrixCellGrey
                ])

        if self.residue and (self.residue.molResidue.molType == 'protein'):
            tlc = ccpCode[:1] + ccpCode[1:].lower()
        else:
            tlc = ccpCode

        i = 0
        for residue in sameTypeResidues:

            j = residues.index(residue)
            neighbourData[i][1] = '%d %s' % (residue.seqCode, tlc)
            if isResidueAssigned(residue):
                assigned += 1
                colorData[i][1] = self.hilightColor

            if j >= residues[0].seqId:
                prevRes = residues[j - 1]
                tlc1 = prevRes.molResidue.ccpCode
                if prevRes.molResidue.molType == 'protein':
                    tlc1 = tlc1[:1] + tlc1[1:].lower()
                neighbourData[i][0] = '%d %s' % (prevRes.seqCode, tlc1)
                if isResidueAssigned(prevRes):
                    colorData[i][0] = self.hilightColor

            if j + 1 < residues[-1].seqId:
                nextRes = residues[j + 1]
                tlc2 = nextRes.molResidue.ccpCode
                if nextRes.molResidue.molType == 'protein':
                    tlc2 = tlc2[:1] + tlc2[1:].lower()
                neighbourData[i][2] = '%d %s' % (nextRes.seqCode, tlc2)
                if isResidueAssigned(nextRes):
                    colorData[i][2] = self.hilightColor
            i += 1

        self.residueFrame.setText('%s Information' % tlc)
        self.assignedLabel.set(self.assignedText + ' %d' % assigned + ' of' +
                               ' %d' % len(sameTypeResidues))
        self.neighbourMatrix.update(objectList=sameTypeResidues,
                                    textMatrix=neighbourData,
                                    colorMatrix=colorData)
        if self.residue:
            #self.specificLabel.set('Chain %s Residue %s %s' % (chain.code,tlc,str(self.residue.seqCode)))
            self.neighbourMatrix.hilightObject(self.residue)

        self.viewResidueFrame.update(self.residue, self.shiftList)
        self.refresh = False
Esempio n. 2
0
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)