Ejemplo n.º 1
0
class SequenceShiftPredictPopup(BasePopup):
    """
  **Predict Protein Shifts from Sequence**
  
  This popup window is designed to allow the prediction of chemical shifts
  for a protein chain from the sequence (so with no structural information),
  using the (external) program CamCoil.

  The Options to select are the Chain for which the prediction is made, and
  the prediction type and the pH used for the prediction, and also the Shift
  List, which is not used for the prediction but is used for the comparison
  with the prediction.

  CamCoil has two variations, one for the prediction of random coil chemical
  shifts and one for prediction of protein loops chemical shifts.

  The Chemical Shift Predictions table lists the atoms in the chain.
  For each atom the data listed is the residue number, residue type, atom
  name, first shift found for that atom in the chosen shiftList, chemical
  shift predicted by CamCoil, and the difference between the actual shift
  and the predicted shift (if both exist).
  
  To run the prediction click on the "Run CamCoil Prediction!" button.  This
  does not store any predicted shifts in the project.

  **Caveats & Tips**

  **References**

  The CamCoil programme:

  http://www-vendruscolo.ch.cam.ac.uk/camcoil.php

  *A. De Simone, A. Cavalli, S-T. D. Hsu, W. Vranken and M. Vendruscolo
  Accurate random coil chemical shifts from an analysis of loop regions in native states of proteins.
  J. Am. Chem. Soc. 131(45):16332-3
  """
    def __init__(self, parent, *args, **kw):

        self.chain = None
        self.shiftList = None
        self.predictionDict = {}

        BasePopup.__init__(
            self,
            parent=parent,
            title='Data Analysis : Predict Shifts from Sequence')

    def body(self, guiFrame):

        self.geometry('700x500')

        guiFrame.expandGrid(1, 0)

        row = 0

        # TOP LEFT FRAME

        frame = LabelFrame(guiFrame, text='Options')
        frame.grid(row=row, column=0, sticky='nsew')
        frame.columnconfigure(7, weight=1)

        label = Label(frame, text='Chain')
        label.grid(row=0, column=0, sticky='w')
        self.chainPulldown = PulldownList(
            frame,
            callback=self.changeChain,
            tipText='Choose the molecular system chain to make predictions for'
        )
        self.chainPulldown.grid(row=0, column=1, sticky='w')

        label = Label(frame, text='Shift List')
        label.grid(row=0, column=2, sticky='w')
        self.shiftListPulldown = PulldownList(
            frame,
            callback=self.changeShiftList,
            tipText='Select the shift list to take input chemical shifts from')
        self.shiftListPulldown.grid(row=0, column=3, sticky='w')

        label = Label(frame, text='Type')
        label.grid(row=0, column=4, sticky='w')
        self.scriptPulldown = PulldownList(
            frame,
            texts=SCRIPT_TEXTS,
            callback=self.changeScript,
            tipText='Select the algorithm script for this chain')
        self.scriptPulldown.grid(row=0, column=5, sticky='w')

        self.pHLabel = Label(frame, text='pH')
        self.pHLabel.grid(row=0, column=6, sticky='w')
        self.pHPulldown = PulldownList(
            frame,
            texts=SCRIPT_PHS,
            tipText='Select the pH to make the prediction for')
        self.pHPulldown.grid(row=0, column=7, sticky='w')

        row += 1

        # BOTTOM LEFT FRAME

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

        tipTexts = [
            'Residue number in chain', 'Residue type code', 'Atom name',
            'Actual shift (first one it finds for atom in chosen shiftList)',
            'CamCoil predicted shift', 'Predicted - Actual'
        ]

        headingList = [
            'Res\nNum', 'Res\nType', 'Atom\nName', 'Actual\nShift',
            'Predicted\nShift', 'Difference'
        ]

        n = len(headingList)
        editWidgets = n * [None]
        editGetCallbacks = n * [None]
        editSetCallbacks = n * [None]

        self.predictionMatrix = ScrolledMatrix(
            frame,
            headingList=headingList,
            tipTexts=tipTexts,
            editWidgets=editWidgets,
            editGetCallbacks=editGetCallbacks,
            editSetCallbacks=editSetCallbacks)
        self.predictionMatrix.grid(row=0, column=0, sticky='nsew')

        row += 1

        tipTexts = [
            'Run the CamCoil method to predict chemical shifts from sequence'
        ]

        texts = ['Run CamCoil Prediction!']
        commands = [self.runCamCoil]
        self.buttonList = createDismissHelpButtonList(guiFrame,
                                                      texts=texts,
                                                      commands=commands,
                                                      help_url=self.help_url,
                                                      expands=True,
                                                      tipTexts=tipTexts)
        self.buttonList.grid(row=row, column=0)

        self.update()

        self.notify(self.registerNotify)

    def destroy(self):

        self.notify(self.unregisterNotify)
        BasePopup.destroy(self)

    def notify(self, notifyfunc):

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

        for func in ('setValue', ):
            notifyfunc(self.updatePredictionMatrixAfter, 'ccp.nmr.Nmr.Shift',
                       func)

    def update(self):

        self.updateShiftListPulldown()
        self.updateChainPulldown()
        self.updatePredictionMatrixAfter()

    def runCamCoil(self):

        chain = self.chain
        script = self.scriptPulldown.getText()
        if script == LFP_SCRIPT:
            pH = ''
        else:
            pH = self.pHPulldown.getText()

        if not chain:
            showError('Cannot Run CamCoil',
                      'Please specify a chain.',
                      parent=self)
            return

        self.predictionDict[chain] = runCamCoil(chain, pH=pH, script=script)

        self.updatePredictionMatrix()

    def updatePredictionMatrixAfter(self, index=None, text=None):

        self.after_idle(self.updatePredictionMatrix)

    def updatePredictionMatrix(self):

        objectList = []
        textMatrix = []

        chain = self.chain
        shiftList = self.shiftList
        if chain:
            atomShiftDict = self.predictionDict.get(chain, {})

            for residue in chain.sortedResidues():
                for atom in residue.sortedAtoms():
                    currShift = shiftList and findFirstAtomShiftInShiftList(
                        atom, shiftList)
                    value = currShift and currShift.value
                    predShift = atomShiftDict.get(atom)
                    if currShift and predShift:
                        delta = predShift - value
                    else:
                        delta = None
                    data = [
                        residue.seqCode, residue.ccpCode, atom.name, value,
                        predShift, delta
                    ]

                    textMatrix.append(data)
                    objectList.append(atom)

        self.predictionMatrix.update(textMatrix=textMatrix,
                                     objectList=objectList)

    def changeChain(self, chain):

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

    def changeShiftList(self, shiftList):

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

    def changeScript(self, script):

        if script == LFP_SCRIPT:
            self.pHLabel.grid_forget()
            self.pHPulldown.grid_forget()
        else:
            self.pHLabel.grid(row=0, column=6, sticky='w')
            self.pHPulldown.grid(row=0, column=7, sticky='w')

    def updateChainPulldown(self, obj=None):

        index = 0
        names = []
        chains = []
        chain = self.chain

        for molSystem in self.project.molSystems:
            msCode = molSystem.code

            for chainA in molSystem.chains:
                residues = chainA.residues

                if not residues:
                    continue

                for residue in residues:
                    # Must have at least one protein residue
                    if residue.molType == 'protein':
                        names.append('%s:%s' % (msCode, chainA.code))
                        chains.append(chainA)
                        break

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

            index = chains.index(chain)

        else:
            chain = None

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

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

    def updateShiftListPulldown(self, obj=None):

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

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

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

        else:
            self.shiftList = None

        self.shiftListPulldown.setup(names, shiftLists, index)
Ejemplo n.º 2
0
class EditPeakFindParamsPopup(BasePopup):
    """
  ** Peak Settings and Non-Interactive Peak Finding **
  
  The purpose of this dialog is to allow the user to select settings for
  finding and integrating peaks, and also to be able to find peaks in an
  arbitrary region that is specified in a table rather than via a spectrum
  window.
  
  ** Find Parameters tab **

  This can be used to specify how peak finding works.

  First of all, you can search for just positive peaks, just negative
  peaks or both, and the default is that it is just positive peaks.
  However, this is further filtered by what the contour levels are.
  If there are no positive contour levels for a given spectrum then
  positive peaks are not found even if this dialog says they can be,
  and similarly if there are no negative contour levels for a given
  spectrum then negative peaks are not found even if this dialog says
  they can be.

  The peak finding algorithm looks for local extrema (maximum for
  positive peaks and minima for negative peaks).  But on a grid there
  are various ways to define what you mean by an extremum.  Suppose
  you are trying to determine if point p is a maximum (similar
  considerations apply for minimum).  You would want the intensity
  at all nearby points to be less than or equal to the intensity at p.
  You can just check points that are just +- one point from p in each
  dimension, or you can also check "diagonal" points.  For
  example, if you are looking at point p = (x, y) in 2D, then the
  former would mean checking the four points (x-1, y), (x+1, y)
  (x, y-1) and (x, y+1), whereas for the latter you would also have
  to check (x-1, y-1), (x-1, y+1), (x+1, y-1) and (x+1, y+1).  In
  N dimensions the "diagonal" method involves checking 3^N-1 points
  whereas the "non-diagonal" method involves checking only 2N points.
  In general the "non-diagonal" method is probably the one to use,
  and it is the default.

  Peaks are only found above (for positive peaks) or below (for negative
  peaks) some threshold.  By default this is determined by the contour level
  for the spectrum.  For positive peaks the threshold is the minimum
  positive contour level, and for negative peaks the threshold is the
  maximum negative contour level.  However these levels can be scaled up
  (or down) using the "Scale relative to contour levels" option (default
  value 1).  For example, if you have drawn the contour levels low to
  show a bit of noise, but do not want the noise picked as peaks, then
  you could select a scale of 2 (or whatever) to increase the threshold.

  The "Exclusion buffer around peaks" is so that in crowded regions you
  do not get too many peaks near one location.  By default the exclusion
  buffer is 1 point in each dimension, but this can be increased to make
  the algorithm find fewer peaks.

  By default the peak finding only looks at the orthogonal region that
  is displayed in the given window where peak finding is taking place.
  Sometimes it looks like a peak should be found because in x, y you
  can see an extremum, but unless it is also an extremum in the orthogonal
  dimensions it is not picked.  You can widen out the points being
  examined in the orthogonal dimensions by using the "Extra thickness in
  orthogonal dims" option, which is specified in points.

  The "Minimum drop factor" is by what factor the intensity needs to drop
  from its extreme value for there to be considered to be a peak.  This
  could help remove sinc wiggle peaks, for example.  The default is that
  the drop factor is 0, which in effect means that there is no condition.

  The "Volume method" is what is used to estimate the volume of peaks that
  are found.  The default is "box sum", which just looks at a fixed size
  box around the peak centre and sums the intensities in that.  The size
  of the box is set in the table in the Spectrum Widths tab.  The
  "truncated box sum" is the same as "box sum" except that the summing
  stops in a given direction when (if) the intensities start increasing.
  The "parabolic" fit fits a quadratic equation in each dimension to the
  intensity at the peak centre and ad +- 1 points and then uses the
  equivalent Gaussian fit to estimate the volume.

  ** Spectrum Widths **

  This can be used to specify minimum linewidths (in Hz) for there to be
  considered a peak to exist in the peak finding algorithm.  It is also
  where the Boxwidth for each dimension in each spectrum is specified.

  ** Diagonal Exclusions **

  This can be used to exclude peaks from being found in regions near
  the diagonal (so in homonuclear experiments).  The exclusion region
  is specified in ppm and is independent of spectrum.

  ** Region Peak Find **

  This can be used to find peaks non-interactively (so not having to
  control shift drag inside a spectrum window).  The region being
  analysed is specified in the table.  There are two types of conditions
  that can be specified, "include" for regions that should be included
  and "exclude" for regions that should be excluded.  The regions are
  specified in ppm.

  The "Whole Region" button will set the selected row in the table to be
  the entire fundamental region of the spectrum.

  The "Add Region" button adds an extra row to the table, and the "Delete
  Region" button removes the selected row.

  The "Adjust Params" button goes to the Find Parameters tab.

  The "Find Peaks!" button does the peak finding.

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

        self.spectrum = None

        BasePopup.__init__(self,
                           parent=parent,
                           title='Peak : Peak Finding',
                           **kw)

    def body(self, guiFrame):

        self.geometry('600x350')

        guiFrame.expandGrid(0, 0)

        tipTexts = ['', '', '', '']
        options = [
            'Find Parameters', 'Spectrum Widths', 'Diagonal Exclusions',
            'Region Peak Find'
        ]
        tabbedFrame = TabbedFrame(guiFrame, options=options, grid=(0, 0))

        frameA, frameB, frameC, frameD = tabbedFrame.frames
        self.tabbedFrame = tabbedFrame

        # Find Params

        frameA.expandGrid(2, 0)

        row = 0
        label = LabelFrame(frameA,
                           text='Extrema to search for:',
                           grid=(row, 0),
                           gridSpan=(1, 2))
        label.expandGrid(0, 1)

        entries = ['positive and negative', 'positive only', 'negative only']
        tipTexts = [
            'Sets whether peak picking within spectra find intensity maxima, minima or both maxima and minima',
        ]
        self.extrema_buttons = RadioButtons(label,
                                            entries=entries,
                                            select_callback=self.apply,
                                            direction='horizontal',
                                            grid=(0, 0),
                                            tipTexts=tipTexts)

        row += 1
        label = LabelFrame(frameA,
                           text='Nearby points to check:',
                           grid=(row, 0),
                           gridSpan=(1, 2))
        label.expandGrid(None, 1)

        entries = ['+-1 in at most one dim', '+-1 allowed in any dim']
        tipTexts = [
            'Sets how permissive the peak picking in when searching for intensity extrema; by adding extra points to the selected search region',
        ]
        self.adjacent_buttons = RadioButtons(label,
                                             entries=entries,
                                             select_callback=self.apply,
                                             direction='horizontal',
                                             grid=(0, 0),
                                             tipTexts=tipTexts)

        row += 1
        labelFrame = LabelFrame(frameA,
                                text='Other parameters:',
                                grid=(row, 0),
                                gridSpan=(1, 2))
        labelFrame.expandGrid(5, 2)

        frow = 0
        label = Label(labelFrame,
                      text='Scale relative to contour levels:',
                      grid=(frow, 0),
                      sticky='e')
        tipText = 'Threshold above which peaks are picked, relative to the lowest displayed contour; 1.0 means picking exactly what is visible'
        self.scale_entry = FloatEntry(labelFrame,
                                      grid=(frow, 1),
                                      tipText=tipText,
                                      returnCallback=self.apply,
                                      width=10)
        self.scale_entry.bind('<Leave>', self.apply, '+')

        frow += 1
        label = Label(labelFrame,
                      text='Exclusion buffer around peaks (in points):',
                      grid=(frow, 0),
                      sticky='e')
        tipText = 'The size of the no-pick region, in data points, around existing picked peaks; eliminates duplicate picking'
        self.buffer_entry = IntEntry(labelFrame,
                                     returnCallback=self.apply,
                                     grid=(frow, 1),
                                     width=10,
                                     tipText=tipText)
        self.buffer_entry.bind('<Leave>', self.apply, '+')

        frow += 1
        label = Label(labelFrame,
                      text='Extra thickness in orthogonal dims (in points):',
                      grid=(frow, 0),
                      sticky='e')
        tipText = 'Sets whether to consider any additional planes (Z dimension) when calculating peak volume integrals'
        self.thickness_entry = IntEntry(labelFrame,
                                        returnCallback=self.apply,
                                        width=10,
                                        grid=(frow, 1),
                                        tipText=tipText)
        self.thickness_entry.bind('<Leave>', self.apply, '+')

        frow += 1
        label = Label(labelFrame,
                      text='Minimum drop factor (0.0-1.0):',
                      grid=(frow, 0),
                      sticky='e')
        tipText = ''
        self.drop_entry = FloatEntry(labelFrame,
                                     returnCallback=self.apply,
                                     width=10,
                                     grid=(frow, 1),
                                     tipText=tipText)
        self.drop_entry.bind('<Leave>', self.apply, '+')

        frow += 1
        label = Label(labelFrame,
                      text='Volume method:',
                      grid=(frow, 0),
                      sticky='e')
        tipText = 'Selects which method to use to calculate peak volume integrals when peaks are picked; box sizes are specified in "Spectrum Widths"'
        self.method_menu = PulldownList(labelFrame,
                                        texts=PeakBasic.PEAK_VOLUME_METHODS,
                                        grid=(frow, 1),
                                        callback=self.apply,
                                        tipText=tipText)

        # Spectrum widths

        frameB.expandGrid(1, 1)

        label = Label(frameB, text='Spectrum: ')
        label.grid(row=0, column=0, sticky='e')

        tipText = 'The spectrum which determines the widths being shown'
        self.expt_spectrum = PulldownList(frameB,
                                          tipText=tipText,
                                          callback=self.setSpectrumProperties)
        self.expt_spectrum.grid(row=0, column=1, sticky='w')

        self.editLinewidthEntry = FloatEntry(self,
                                             text='',
                                             returnCallback=self.setLinewidth,
                                             width=10)
        self.editBoxwidthEntry = FloatEntry(self,
                                            text='',
                                            returnCallback=self.setBoxwidth,
                                            width=10)
        tipTexts = [
            'The number of the spectrum dimension to which the settings apply',
            'The nuclear isotope measures in the spectrum dimension',
            'The smallest value for the linewidth of a peak for it to be picked',
            'The size of the spectrum region to perform the volume integral over'
        ]
        headingList = [
            'Dimension', 'Isotope', 'Minimum Linewidth (Hz)', 'Boxwidth'
        ]
        editSetCallbacks = [None, None, self.setLinewidth, self.setBoxwidth]
        editGetCallbacks = [None, None, self.getLinewidth, self.getBoxwidth]
        editWidgets = [
            None, None, self.editLinewidthEntry, self.editBoxwidthEntry
        ]
        self.spectrumMatrix = ScrolledMatrix(frameB,
                                             initialRows=6,
                                             editSetCallbacks=editSetCallbacks,
                                             editGetCallbacks=editGetCallbacks,
                                             editWidgets=editWidgets,
                                             headingList=headingList,
                                             callback=self.selectCell,
                                             tipTexts=tipTexts)
        self.spectrumMatrix.grid(row=1, column=0, columnspan=2, sticky='nsew')

        # Diagonal Exclusions

        frameC.expandGrid(0, 0)

        tipTexts = [
            'The isotope as measures on the axis of a spectrum window',
            'The distance from the homonuclear diagonal line within which no peak picking can occur'
        ]
        self.exclusionEntry = FloatEntry(self,
                                         text='',
                                         returnCallback=self.setExclusion,
                                         width=10)
        headingList = ['Isotope', 'Diagonal Exclusion (ppm)']
        editSetCallbacks = [None, self.setExclusion]
        editGetCallbacks = [None, self.getExclusion]
        editWidgets = [None, self.exclusionEntry]
        self.isotopeMatrix = ScrolledMatrix(frameC,
                                            editSetCallbacks=editSetCallbacks,
                                            editGetCallbacks=editGetCallbacks,
                                            editWidgets=editWidgets,
                                            headingList=headingList,
                                            grid=(0, 0),
                                            tipTexts=tipTexts)

        # Region peak find

        self.regionFindPeakList = None
        self.regionCondition = None
        self.regionConditions = []
        self.regionCol = 1

        row = 0

        label = Label(frameD, text='Peak List: ', grid=(0, 0))
        tipText = 'Selects which peak list to perform region-wide peak picking for'
        self.regionPeakListPulldown = PulldownList(
            frameD,
            callback=self.changeRegionPeakList,
            grid=(0, 1),
            tipText=tipText)

        row += 1
        frameD.expandGrid(row, 1)

        self.regionEntry = FloatEntry(self,
                                      text='',
                                      returnCallback=self.setRegion,
                                      width=10)
        self.conditionMenu = PulldownList(self,
                                          texts=('include', 'exclude'),
                                          callback=self.setCondition)

        tipTexts = [
            'Whether to include or exclude the states region from region-wide peak picking',
        ]
        headingList = ['Condition']
        editSetCallbacks = [None]
        editGetCallbacks = [None]
        editWidgets = [self.conditionMenu]
        self.regionFindMatrix = ScrolledMatrix(
            frameD,
            headingList=headingList,
            callback=self.selectRegionCell,
            editWidgets=editWidgets,
            editGetCallbacks=editGetCallbacks,
            editSetCallbacks=editSetCallbacks,
            grid=(row, 0),
            gridSpan=(1, 2))

        row += 1
        tipTexts = [
            'Sets the currently selected region row to cover the whole spectrum',
            'Add a new region row, which may them be set for exclusion or inclusion when peak picking large areas',
            'Remove the selected region specification',
            'Go to the panel for setting the parameters that control how peaks extrema are picked',
            'Using the stated regions and parameters, perform region-wide peak picking'
        ]
        texts = [
            'Whole Region', 'Add Region', 'Delete Region', 'Adjust Params',
            'Find Peaks!'
        ]
        commands = [
            self.wholeRegion, self.addCondition, self.deleteCondition,
            self.adjustParams, self.regionFindPeaks
        ]

        buttons = ButtonList(frameD,
                             texts=texts,
                             commands=commands,
                             grid=(row, 0),
                             gridSpan=(1, 2),
                             tipTexts=tipTexts)
        buttons.buttons[4].config(bg='#B0FFB0')

        utilButtons = UtilityButtonList(tabbedFrame.sideFrame,
                                        grid=(0, 0),
                                        helpUrl=self.help_url,
                                        sticky='e')

        self.dataDim = None
        self.setParamsEntries()
        self.updateSpectrum()
        self.setIsotopeProperties()
        self.updateRegionPeakLists()

        self.administerNotifiers(self.registerNotify)

    def administerNotifiers(self, notifyFunc):

        # Many more needed here, esp on the AnalysisProject prams

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

        for func in ('__init__', 'delete'):
            notifyFunc(self.updateRegionPeakLists, 'ccp.nmr.Nmr.PeakList',
                       func)

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

        for func in ('setPeakFindBoxWidth', 'setPeakFindMinLineWidth'):
            notifyFunc(self.updateSpectrumTable,
                       'ccpnmr.Analysis.AnalysisDataDim', func)

    def destroy(self):

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

    def updateSpectrum(self, spectrum=None):

        if not spectrum:
            spectrum = self.spectrum

        spectra = self.parent.getSpectra()
        if spectra:
            if spectrum not in spectra:
                spectrum = spectra[0]
            index = spectra.index(spectrum)
            names = ['%s:%s' % (x.experiment.name, x.name) for x in spectra]
        else:
            index = 0
            names = []

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

        self.setSpectrumProperties(spectrum)

    def updateNotifier(self, *extra):

        self.updateSpectrum()

    def setLinewidth(self, *event):

        value = self.editLinewidthEntry.get()
        if value is not None:
            PeakFindParams.setPeakFindMinLinewidth(self.dataDim, value)
            self.setSpectrumProperties(self.dataDim.dataSource)

    def getLinewidth(self, dataDim):

        if dataDim:
            self.editLinewidthEntry.set(
                PeakFindParams.getPeakFindMinLinewidth(self.dataDim))

    def setBoxwidth(self, *event):

        value = self.editBoxwidthEntry.get()
        if value is not None:
            PeakFindParams.setPeakFindBoxwidth(self.dataDim, value)
            self.setSpectrumProperties(self.dataDim.dataSource)

    def getBoxwidth(self, dataDim):

        if dataDim:
            self.editBoxwidthEntry.set(
                PeakFindParams.getPeakFindBoxwidth(self.dataDim))

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

        self.dataDim = object

    def setExclusion(self, *extra):

        isotope = self.isotopeMatrix.currentObject
        if not isotope:
            return

        value = self.exclusionEntry.get()
        if value is not None:
            setIsotopeExclusion(isotope, value)
            self.setIsotopeProperties()

    def getExclusion(self, isotope):

        value = getIsotopeExclusion(isotope)
        self.exclusionEntry.set(value)

    def setParamsEntries(self):

        project = self.project
        params = PeakFindParams.getPeakFindParams(project)

        self.scale_entry.set(params['scale'])
        self.buffer_entry.set(params['buffer'])
        self.thickness_entry.set(params['thickness'])
        self.drop_entry.set(params['drop'])
        volumeMethod = params['volumeMethod']
        if volumeMethod == 'parabolic fit':
            volumeMethod = PeakBasic.PEAK_VOLUME_METHODS[0]
        self.method_menu.set(params['volumeMethod'])

        if (params['nonadjacent']):
            n = 1
        else:
            n = 0
        self.adjacent_buttons.setIndex(n)

        have_high = params['haveHigh']
        have_low = params['haveLow']
        if (have_high and have_low):
            n = 0
        elif (have_high):
            n = 1
        else:
            n = 2
        self.extrema_buttons.setIndex(n)

    def apply(self, *extra):

        params = {}
        params['scale'] = self.scale_entry.get()
        params['buffer'] = self.buffer_entry.get()
        params['thickness'] = self.thickness_entry.get()
        params['drop'] = self.drop_entry.get()
        params['volumeMethod'] = self.method_menu.getText()

        n = self.adjacent_buttons.getIndex()
        if (n == 0):
            nonadjacent = False
        else:
            nonadjacent = True
        params['nonadjacent'] = nonadjacent

        n = self.extrema_buttons.getIndex()
        if (n == 0):
            have_high = True
            have_low = True
        elif (n == 1):
            have_high = True
            have_low = False
        elif (n == 2):
            have_high = False
            have_low = True
        params['haveHigh'] = have_high
        params['haveLow'] = have_low

        project = self.project
        try:
            PeakFindParams.setPeakFindParams(project, params)
        except Implementation.ApiError, e:
            showError('Parameter error', e.error_msg, parent=self)
Ejemplo n.º 3
0
class CreateContourFilePopup(BasePopup):

  """
  **Create Contour Files for Project**

  The purpose of this dialog is to allow the user to create new contour
  files for the project.  Not only the spectrum has to be specified, but
  also the two dimensions (X, Y) that are going to be contoured.  The
  table specifies the conditions that specify the region being contoured,
  in terms of what is included and excluded.

  For now, only one condition (which is an "include" condition) is allowed.
  This also means that the "Add Condition" and "Delete Condition" buttons
  are disabled.

  The contour directory is specified by the program, using the project
  directory but the file name can be specified by the user.

  The contour levels used are the ones that are current for the spectrum,
  as set in the `Contour Levels dialog`_.

  See also: `Spectrum Contour Files`_, `Add Existing Contour Files`_.

  .. _`Contour Levels dialog`: EditContourLevelsPopup.html
  .. _`Spectrum Contour Files`: EditContourFilesPopup.html
  .. _`Add Existing Contour Files`: AddContourFilePopup.html
"""

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

    self.spectrumConditions = []
    self.col = None
    self.spectrum = None

    BasePopup.__init__(self, parent=parent, title='New Contour File', **kw)

  def body(self, master):

    self.geometry('650x200')

    master.grid_columnconfigure(1, weight=1)

    row = 0
    frame = Frame(master, grid=(row, 0), gridSpan=(1,2))
    
    label = Label(frame, text='Spectrum: ', grid=(row, 0))
    tipText = 'Selects the experiment and spectrum for which to make a contour file'
    self.expt_spectrum = PulldownList(frame, grid=(row, 1),
                                      tipText=tipText, callback=self.update)

    label = Label(frame, text=' (X-dim, Y-dim): ', grid=(row, 2))
    tipText = 'Selects which dimensions (projection) of the spectrum form the X and Y axes of the contour file'
    self.dim_menu = PulldownList(frame, grid=(row, 3), tipText=tipText,
                                 callback=self.updateFile)

    row = row + 1
    master.grid_rowconfigure(row, weight=1)
    #### for now not editable because only allow one include region
    ###self.conditionMenu = PulldownMenu(self, entries=('include', 'exclude'),
    ###                       callback=self.selectedCondition, do_initial_callback=False)
    self.regionEntry = FloatEntry(self, text='', returnCallback=self.setRegion, width=10)
    tipTexts = ['Whether to include or exclude the region in the contour file',
                '',
                '',
                '']
    headingList = ['Condition','','','','']
    editSetCallbacks = [None]
    editGetCallbacks = [None]
    ###editWidgets = [self.conditionMenu]
    editWidgets = [None]
    self.conditionTable = ScrolledMatrix(master, initialRows=6,
                                         grid=(row, 0), gridSpan=(1,2),
                                         tipTexts=tipTexts,
                                         headingList=headingList,
                                         callback=self.selectCell,
                                         editWidgets=editWidgets,
                                         editGetCallbacks=editGetCallbacks,
                                         editSetCallbacks=editSetCallbacks)

    # TBD: make directory editable
    row = row + 1
    label = Label(master, text='Contour dir: ', grid=(row, 0), sticky='e')
    tipText = 'The directory location on disk into which contour files are saved'
    self.dir_label = Label(master, text='', grid=(row, 1), tipText=tipText)

    row = row + 1
    label = Label(master, text='File name: ', grid=(row, 0), sticky='e')
    tipText = 'Sets the name of the contour file to save to disk'
    self.file_entry = Entry(master, grid=(row, 1), tipText=tipText)

    ##row = row + 1
    ##label = Label(master, text='Contour levels: ')
    ##label.grid(row=row, column=0, sticky='e')
    ##self.levels_entry = FloatEntry(master, isArray=True, returnCallback=self.saveLevels)
    ##self.levels_entry.grid(row=row, column=1, sticky='ew')

    row = row + 1
    tipTexts = ['Add a new contour region for including in or excluding from the contour file ',
                'Remove the selected contour region from the table',
                'Make the specified contour file using the input settings & regions']
    texts = [ 'Add Condition', 'Delete Condition', 'Contour and Save' ]
    commands = [ self.addCondition, self.deleteCondition, self.contourAndSaveSpectrum ]
    self.buttons = UtilityButtonList(master, texts=texts, commands=commands,
                                     doClone=False, helpUrl=self.help_url,
                                     grid=(row, 0), gridSpan=(1,2), tipTexts=tipTexts)

    ### TBD disabled for now
    for n in range(2):
      self.buttons.buttons[n].config(state='disabled')

    self.curateNotifiers(self.registerNotify)
    self.updateSpectrum()
    self.updateDimMenu()

  def destroy(self):

    self.curateNotifiers(self.unregisterNotify)

    BasePopup.destroy(self)

  def curateNotifiers(self, notifyFunc):

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

  def update(self, spectrum):

    if spectrum is self.spectrum:
      return

    self.spectrum = spectrum

    self.updateDimMenu()
    self.updateConditionTable()
    self.updateFile()
    ##self.updateLevels()

  def updateSpectrum(self, spectrum=None):

    if not spectrum:
      spectrum = self.spectrum

    spectra = self.parent.getSpectra()
    if spectra:
      if spectrum not in spectra:
        spectrum = spectra[0]
      index = spectra.index(spectrum)
      names = ['%s:%s' % (x.experiment.name, x.name) for x in spectra]
    else:
      index = 0
      names = []

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

    self.update(spectrum)

  def updateNotifier(self, *extra):

    self.updateSpectrum()

  def updateFile(self, *extra):

    spectrum = self.spectrum
    if not spectrum:
      return

    analysisSpectrum = spectrum.analysisSpectrum
    if not analysisSpectrum:
      return

    dims = self.dim_menu.getText()
    dims = [int(dim) for dim in dims.split(', ')]
    (xdim, ydim) = dims
    storedContour = analysisSpectrum.findFirstStoredContour(dims=dims)
    if not storedContour:
      storedContour = analysisSpectrum.findFirstStoredContour()
    if storedContour:
      fileName = storedContour.path
    else:
      fileName = '%s_%s_%d_%d.cnt' % (spectrum.experiment.name, spectrum.name, xdim, ydim)

    path = self.getContourDir()
    self.dir_label.set(path)
    self.file_entry.set(fileName)

  def getContourDir(self):

    spectrum = self.spectrum
    if not spectrum:
      return ''

    analysisSpectrum = spectrum.analysisSpectrum
    if not analysisSpectrum:
      return ''

    url = analysisSpectrum.contourDir
    if url: # should be the case since set in Analysis.py
      path = url.dataLocation
    else:
      path = ''

    return path

  """
  def saveFile(self, *extra):

    spectrum = self.spectrum
    if not spectrum:
      return

    analysisSpectrum = spectrum.analysisSpectrum
    if not analysisSpectrum:
      return

    fileName = self.file_entry.get()
    if fileName:
      
      application = self.project.application
      application.setValue(spectrum, keyword='contourFileName', value=fileName)
"""
    
  def updateDimMenu(self):

    entries = []
    spectrum = self.spectrum
    if spectrum:
      ndim = spectrum.numDim
      for xdim in range(1, ndim):
        dataDim = spectrum.findFirstDataDim(dim=xdim)
        if dataDim.className != 'SampledDataDim':
          for ydim in range(xdim+1, ndim+1):
            dataDim = spectrum.findFirstDataDim(dim=ydim)
            if dataDim.className != 'SampledDataDim':
              entries.append('%d, %d' % (xdim, ydim))
    self.dim_menu.setup(entries, objects=None, index=0)

  def updateConditionTable(self, *extra):

    spectrum = self.spectrum
    if spectrum:
      ndim = spectrum.numDim
    else:
      ndim = 0
    tipTexts = ['Whether to include or exclude the region in the contour file',]
    headingList = ['Condition']
    ###editWidgets = [self.conditionMenu] + 2*ndim*[self.regionEntry]
    ###editGetCallbacks = [self.getCondition]
    ###editSetCallbacks = [self.setCondition]
    editWidgets = [None] + 2*ndim*[self.regionEntry]
    editGetCallbacks = [None]
    editSetCallbacks = [None]
    for i in range(ndim):
      dim = i + 1
      headingList.extend(['Dim %d Min' % dim, 'Dim %d Max' % dim])
      editGetCallbacks.append(lambda obj, i=i: self.getRegionMin(obj, i))
      editGetCallbacks.append(lambda obj, i=i: self.getRegionMax(obj, i))
      editSetCallbacks.append(lambda obj, i=i: self.setRegionMin(obj, i))
      editSetCallbacks.append(lambda obj, i=i: self.setRegionMax(obj, i))
      tipTexts.append('Lower bound of dimension %d region to include or exclude' % dim)
      tipTexts.append('Upper bound of dimension %d region to include or exclude' % dim)
      
    objectList = []
    textMatrix = []
    self.spectrumConditions = self.getSpectrumConditions()
    for spectrumCondition in self.spectrumConditions:
      (condition, region) = spectrumCondition
      objectList.append(spectrumCondition)
      textRow = [condition]
      for i in range(ndim):
        r = region[i]
        if r:
          rMin = r[0]
          rMax = r[1]
        else:
          rMin = rMax = None
        textRow.append(rMin)
        textRow.append(rMax)
      textMatrix.append(textRow)

    self.conditionTable.update(objectList=objectList, textMatrix=textMatrix,
                               headingList=headingList,
                               editSetCallbacks=editSetCallbacks,
                               editGetCallbacks=editGetCallbacks,
                               editWidgets=editWidgets)

  """
  def updateLevels(self):

    spectrum = self.getSpectrum()
    if spectrum:
      analysisSpectrum = spectrum.analysisSpectrum
      levels = analysisSpectrum.posLevels + analysisSpectrum.negLevels
      scale = spectrum.scale / self.analysisProject.globalContourScale
      levels = [ level/scale for level in levels ]
    else:
      levels = []

    self.levels_entry.set(levels)

  def saveLevels(self):

    spectrum = self.spectrum
    if not spectrum:
      return
"""

  def getRegionMin(self, spectrumCondition, dim):
   
    #print 'getRegionMin'
    (condition, region) = spectrumCondition
    self.regionEntry.set(region[dim][0])

  def setRegionMin(self, spectrumCondition, dim):
   
    #print 'setRegionMin'

    (condition, region) = spectrumCondition
    (r0, r1) = region[dim]
    r = self.regionEntry.get()
    region[dim] = (r, r1)
    self.setSpectrumConditions()
    self.updateConditionTable()

  def getRegionMax(self, spectrumCondition, dim):
   
    #print 'getRegionMax'
    (condition, region) = spectrumCondition
    self.regionEntry.set(region[dim][1])

  def setRegionMax(self, spectrumCondition, dim):

    #print 'setRegionMax'
    (condition, region) = spectrumCondition
    (r0, r1) = region[dim]
    r = self.regionEntry.get()
    region[dim] = (r0, r)
    self.setSpectrumConditions()
    self.updateConditionTable()

  def setRegion(self, *extra):

    spectrumCondition = self.getCurrentObject()
    if spectrumCondition and self.col is not None:
      col = self.col - 1 # -1 because of condition
      dim = col / 2
      if col % 2: # max
        self.setRegionMax(spectrumCondition, dim)
      else: # min
        self.setRegionMin(spectrumCondition, dim)

  """ not needed for now
  def getCondition(self, spectrumCondition):
   
    #print 'getCondition'
    (condition, region) = spectrumCondition
    self.conditionMenu.set(condition)

  def setCondition(self, spectrumCondition):

    #print 'setCondition'
    spectrumCondition[0] = self.conditionMenu.get()
    self.setSpectrumConditions()

  def selectedCondition(self, ind, condition):

    spectrumCondition = self.getCurrentObject()

    if spectrumCondition is not None:
      self.setCondition(spectrumCondition)
"""

  def getSpectrumConditions(self):

    spectrum = self.spectrum
    if not spectrum:
      return []

    application = self.project.application
    spectrumConditions = application.getValue(spectrum, keyword='contourFileConditions')
    if spectrumConditions:
      spectrumConditions = eval(spectrumConditions)
    else:
      region = []
      for i in range(spectrum.numDim):
        dim = i + 1
        region.append(self.getWholeRegion(spectrum, dim))
      spectrumCondition = ['include', region]
      spectrumConditions = [spectrumCondition]
      self.setSpectrumConditions(spectrumConditions)

    return spectrumConditions

  def setSpectrumConditions(self, spectrumConditions = None):

    spectrum = self.spectrum

    if spectrum:
      if not spectrumConditions:
        spectrumConditions = self.spectrumConditions

      application = self.project.application
      application.setValue(spectrum, keyword='contourFileConditions', value=str(spectrumConditions))

  def getDimMin(self, spectrum, dim):

    dataDim = spectrum.findFirstDataDim(dim=dim)
    if dataDim.className == 'SampledDataDim':
      r = 1.0
    else:
      converter = UnitConverter.pnt2ppm
      dataDimRef = ExperimentBasic.getPrimaryDataDimRef(dataDim)
      r = converter(float(dataDim.numPoints), dataDimRef)

    return r

  def getDimMax(self, spectrum, dim):

    dataDim = spectrum.findFirstDataDim(dim=dim)
    if dataDim.className == 'SampledDataDim':
      r = float(dataDim.numPoints)
    else:
      converter = UnitConverter.pnt2ppm
      dataDimRef = ExperimentBasic.getPrimaryDataDimRef(dataDim)
      r = converter(1.0, dataDimRef)

    return r

  def getWholeRegion(self, spectrum, dim):

    rMin = self.getDimMin(spectrum, dim)
    rMax = self.getDimMax(spectrum, dim)

    return (rMin, rMax)

  def getCurrentObject(self):

    # sometimes row highlighting stops so obj passed into selectCell might no longer be current
    # so instead use direct interrogation of scrolledMatrix
    obj = self.conditionTable.currentObject

    return obj

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

    # see note about obj in getCurrentObject()
    self.col = col

  def addCondition(self):

    spectrum = self.spectrum
    if spectrum:
      ndim = spectrum.numDim
      spectrumCondition = ['exclude', ndim*[(None,None)]]
      self.spectrumConditions.append(spectrumCondition)
      self.setSpectrumConditions()
      self.updateConditionTable()

  def deleteCondition(self):

    spectrumCondition = self.getCurrentObject()
    if spectrumCondition:
      self.spectrumConditions.remove(spectrumCondition)
      self.setSpectrumConditions()
      self.updateConditionTable()

  def continueIfFileNameUsed(self, fileName):

    result = True

    storedContoursToDelete = []
    for analysisSpectrum in self.analysisProject.analysisSpectra:
      for storedContour in analysisSpectrum.storedContours:
        if storedContour.fullPath == fileName:
          storedContoursToDelete.append(storedContour)

    if storedContoursToDelete:
      if len(storedContoursToDelete) == 1:
        s = ''
        t = 's'
      else:
        s = 's'
        t = ''
      result = showYesNo('File used', 'Stored contour%s already use%s this fileName, and will be deleted: continue?' % (s, t), parent=self)
      if result:
        for storedContour in storedContoursToDelete:
          try:
            storedContour.delete()
          except:
            pass

    return result

  def contourAndSaveSpectrum(self):

    spectrum = self.spectrum
    if not spectrum:
      return

    if not self.spectrumConditions:
      return

    ###self.saveFile()
    fileName = self.file_entry.get()
    if not fileName:
      showError('No filename', 'No filename given', parent=self)
      return

    contourDir = self.getContourDir()

    fullPath = joinPath(contourDir, fileName)
    if not self.continueIfFileNameUsed(fullPath):
      return

    directory = os.path.dirname(fullPath)
    if not os.path.exists(directory):
      os.makedirs(directory)

    dims = self.dim_menu.getText()
    (xdim, ydim) = [int(dim) for dim in dims.split(', ')]

    ##self.saveLevels()
    ##levels = self.levels_entry.get()
    ##if not levels:
    ##  showError('No levels', 'No contour levels given', parent=self)
    ##  return
    analysisSpectrum = spectrum.analysisSpectrum
    levels = analysisSpectrum.posLevels + analysisSpectrum.negLevels
    scale = spectrum.scale / self.analysisProject.globalContourScale
    levels = [ level/scale for level in levels ]

    spectrumCondition = self.spectrumConditions[0]
    (condition, region) = spectrumCondition

    ndim = spectrum.numDim
    firstInt = ndim * [0]
    lastInt = ndim * [0]
    for i in range(ndim):
      try:
        (firstInt[i], lastInt[i]) = self.convertToPoints(spectrum, i, region[i])
      except Exception, e:
        showError('Invalid region', str(e), parent=self)

    try:
      #print 'about to saveSpectrumContours', fullPath, xdim, ydim, levels, firstInt, lastInt
      saveSpectrumContours(spectrum, fullPath, xdim, ydim, levels, firstInt, lastInt,
                         mem_cache=self.parent.mem_cache)
    except Exception, e:
      showError('Save error', str(e), parent=self)
      return
Ejemplo n.º 4
0
class ItemSelectPopup(BasePopup):
    def __init__(self,
                 parent,
                 entries,
                 label='',
                 message='',
                 select_text='Select',
                 default=0,
                 *args,
                 **kw):

        self.entries = entries
        self.label = label
        self.message = message
        self.select_text = select_text
        self.default = default
        self.item = None

        kw['title'] = 'Select Item'
        kw['transient'] = True
        kw['modal'] = True
        BasePopup.__init__(self, parent=parent, *args, **kw)

    def body(self, guiFrame):

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

        row = 0
        if self.message:
            label = Label(guiFrame,
                          text=self.message,
                          gridSpan=(1, 2),
                          grid=(row, 0))
            row += 1

        if self.label:
            label = Label(guiFrame, text=self.label, grid=(row, 0))

        self.itemMenu = PulldownList(guiFrame,
                                     texts=self.entries,
                                     objects=self.entries,
                                     index=self.default,
                                     grid=(row, 1))

        row += 1
        texts = [self.select_text, 'Cancel']
        commands = [self.ok, self.cancel]
        buttons = ButtonList(guiFrame,
                             texts=texts,
                             commands=commands,
                             gridSpan=(1, 2),
                             grid=(row, 0))

    def cancel(self):

        self.destroy()

        return None

    def apply(self):

        self.item = self.itemMenu.getText()

        return True
Ejemplo n.º 5
0
class IsotopeSchemeEditor(BasePopup):
    """
  **Create and Edit Per-residue Reference Isotope Schemes**
  
  This system allows the user to create schemes that describe particular
  patterns of atomic isotope labelling in terms of combinations of isotopically
  labelled forms of residues. Once constructed, these schemes may then be
  applied to a molecule of known residue sequence to gauge the levels of
  spin-active isotope incorporation in an NMR experiment. This information is
  useful in several places withing Analysis, including giving more intelligent
  assignment options and in the generation of distance restraints by matching
  peak positions to chemical shifts. Although the schemes may be used directly
  they are typically used as reference information for configuring the `Isotope
  Labelling`_ system; where isotope labelling patterns are tied to particular
  molecules and experiments.

  Because all of the different isotope labelled versions (isotopomers) of each
  residue type are described independently, a scheme can be used to estimate the
  specific amounts of incorporation present at multiple atom sites at the same
  time. For example, although a residue type may have significant levels of 13C
  at the CA and CB positions on average, there may be no form of the residue
  where CA and CB are labelled at the same time, and thus CA-CB correlations
  would not be observed in NMR.

  This popup window is divided into three main tabs, the first describes the
  overall schemes that are available; that would be applied to a molecule in a
  given situation. The second tab details the residue isotopomer components
  within the selected scheme, i.e. which labelled residue forms are present. The
  last tab displays isotopomer labelling in a graphical, three-dimensional way.
  If any isotope labelling schemes have been created or edited the user may 
  immediately save these to disk via the [Save Schemes] button to the right of
  the tabs, although these will naturally be saved when the main CCPN project
  is.

  **Reference Schemes**

  This table lists all of the reference isotope schemes that are available to
  the project. A number of standard schemes are included by default, as part of
  the main CCPN installation. However, the user is free to create new schemes,
  either from a completely blank description or by copying and modifying one of
  the existing schemes. By selecting on a isttope scheme row in the table the
  scheme is selected to be active for the whole popup and the user can see the
  contents of the scheme via the other two tabs.

  It should be noted that the user cannot edit the standard schemes provided by
  CCPN, given that these are stored with the software. Any new or copied schemes
  that the user creates will be stored inside the current CCPN project. If a new
  scheme should be made available to multiple projects, its XML file can be
  copied into the main CCPN installation, if the user has appropriate write
  access.

  **Isotopomers**
  
  The middle tab allows the user to view, and where appropriate edit, the
  isotope labelling descriptions for the residues within the current scheme
  (selected in the pulldown menu at the top). An isotope scheme is constructed
  by specifying one or more isotopomers for each residue type. Each isotopomer
  represents a different way of incorporating spin-active atom labels into a
  given kind of residue. Often there will only be one labelled form of a
  residue, and hence one isotopomer. However, with some kinds of isotope
  enrichment, for example using glycerol 13C labelled at the C2 position,
  connected labelled and unlabelled atom sites can be incorporated in
  alternative ways, resulting in distinct forms of labelling patterns that are
  not the result of a pure random mix. Knowing which labels are present at the
  same time, in the same isotopomer form, can be very important for determining
  which NMR correlations are possible.

  In general use when looking through the default, immutable reference schemes
  that come with CCPN the user can scroll through the isotopomer versions of
  each residue in the upper table. By clicking on one of these rows the lower
  table is filled with details of the amount of each kind of isotope (on
  average) at each atom site. For the lower "Atom Labels" table only one kind of
  chemical element is shown at a time, but the user may switch to  a different
  one via the "Chemical Element" pulldown.

  **Editing Isotopomers**

  When dealing with copied or new isotope schemes the user is allowed to 
  edit all aspects of the scheme. With a completely new scheme there will be no
  isotopomer records to start with and it is common practice to fill in a
  standard set of isotopomers, one for each residue type, made with a base level
  of isotope incorporation. To set this base level the user can use [Set Default
  Abundances] to manually specify values, although the default is to use natural
  abundance levels, which is appropriate in most circumstances. With the base
  levels set the [Add Default Abundance Set] will automatically fill-in a
  starting set of isotopomers for the scheme. Extra isotopomers can be added for
  a specific type of residue via the [Add New:] function and adjacent pulldown
  menu or by copying existing ones; whichever is easier. Each isotopomer has an
  editable weight to enable the user to indicate the relative abundance within a
  given residue type.

  Once a new isotopomer specification is created clicking on its row allows the 
  user to specify the isotope labelling pattern in the lower "Atom Labels"
  table. Here the user selects which kind of chemical element to consider and
  then  double-clicks to edit the "Weighting" columns in the table. The
  weightings represent the relative abundance of a given nuclear isotope at a
  given atom site. The weightings could be set as ratios, fractions, or
  percentages; it is only the relative proportion that is important. For example
  if a carbon atom site was known to have 5% Carbon-12 and 95% Carbon-13
  isotopes then the respective weights could be entered as 1 & 19 or  0.05 &
  0.95; whatever is most convenient. For efficient setup of schemes the
  [Propagate Abundances] function can be used to spread the same levels of
  incorporation over several atom sites (from the last selected row).

  **Isotopomer Structure**
  
  The last tab is an alternative way of presenting the isotope patterns present
  within the residues of the current scheme (selected in either of the first two
  tabs). Here the user selects a residue type in the upper left pulldown menu
  and then a numbered isotopomer, or an average of all isotopomers, in the right
  hand pulldown menu. The structural display will show a moveable picture of the
  residue (in a standard conformation) where unlabelled atom sites are
  represented with grey spheres, labelled sites with yellow spheres and
  intermediate incorporation with shades in between.

  It should be noted that this kind of 3D display is only possible if there is
  an idealised structure available for a residue type. This data will be 
  present for all of the regular biopolymer residues, but may be missing for
  more unusual compounds; although a lack of coordinates does not impact upon
  the isotopomer setup.

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

  * Translate: Arrow keys + Control key

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

  * Translate: Middle button click & drag + Control key

  Also an options menu appears when the right mouse button is clicked.

  .. _`Isotope Labelling`: EditMolLabellingPopup.html
 
  """
    def __init__(self, parent, project=None, *args, **kw):

        if not project:
            self.project = Implementation.MemopsRoot(
                name='defaultUtilityProject')
        else:
            self.project = project

        self.waiting = False
        self.waitingAtom = False
        self.molType = 'protein'
        self.scheme = None
        self.isotopomer = None
        self.isotopomerV = False  # Not None
        self.ccpCodeV = None
        self.element = 'C'
        self.atomLabelTuple = None
        self.isotopes = [x[0] for x in getSortedIsotopes(self.project, 'C')]
        self.defaultAbun = {}

        BasePopup.__init__(self,
                           parent=parent,
                           title='Molecule : Reference Isotope Schemes',
                           **kw)

    def body(self, guiFrame):

        self.geometry('700x600')

        guiFrame.expandGrid(0, 0)

        tipTexts = [
            'A table of all of the reference isotope scheme definitions available to the project',
            'A list of the residue isotopomers that comprise the selected isotope labelling scheme',
            'A three-dimensional representation of residues and their isotopomer labelling'
        ]

        options = ['Reference Schemes', 'Isotopomers', 'Isotopomer Structure']

        tabbedFrame = TabbedFrame(guiFrame,
                                  options=options,
                                  grid=(0, 0),
                                  tipTexts=tipTexts)

        self.tabbedFrame = tabbedFrame
        frameA, frameB, frameC = tabbedFrame.frames

        #
        # Schemes
        #

        frameA.expandGrid(0, 0)

        tipTexts = [
            'A short textual code that identifies the reference isotope scheme in graphical displays',
            'The full name for the isotope scheme',
            'A detailed description of the isotope scheme including user comments',
            'The name of the CCPN data repository in which the isotope scheme is saved; "refData" is in the CCPn installation'
        ]
        headingList = ['Code', 'Name', 'Description', 'Save Location']
        self.schemeNameEntry = Entry(self,
                                     text='',
                                     returnCallback=self.setSchemeName,
                                     width=20)
        self.schemeDetailsEntry = Entry(self,
                                        text='',
                                        returnCallback=self.setSchemeDetails,
                                        width=20)
        editWidgets = [
            None, self.schemeNameEntry, self.schemeDetailsEntry, None
        ]
        editGetCallbacks = [
            None, self.getSchemeName, self.getSchemeDetails, None
        ]
        editSetCallbacks = [
            None, self.setSchemeName, self.setSchemeDetails, None
        ]

        self.schemeMatrix = ScrolledMatrix(frameA,
                                           headingList=headingList,
                                           callback=self.selectScheme,
                                           editWidgets=editWidgets,
                                           editSetCallbacks=editSetCallbacks,
                                           editGetCallbacks=editGetCallbacks,
                                           multiSelect=False,
                                           grid=(0, 0),
                                           tipTexts=tipTexts)
        self.schemeMatrix.doEditMarkExtraRules = self.schemeEditRules

        tipTexts = [
            'Make a new reference isotope scheme definition based on a copy of the scheme currently selected',
            'Delete the selected isotope scheme',
            'Make a new, blank isotope scheme'
        ]
        texts = ['Copy', 'Delete', 'New']
        commands = [self.copyScheme, self.removeScheme, self.makeNewScheme]
        self.schemeButtons = ButtonList(frameA,
                                        texts=texts,
                                        commands=commands,
                                        grid=(1, 0),
                                        tipTexts=tipTexts)

        #
        # Isotopomers
        #

        frameB.expandGrid(3, 0)

        row = 0
        frame = Frame(frameB, grid=(row, 0))
        frame.expandGrid(0, 2)

        tipText = 'Selects which of the available isotope schemes to view/edit'
        label = Label(frame, text='Reference Scheme:', grid=(0, 0))
        self.schemePulldown = PulldownList(frame,
                                           callback=self.setLabellingScheme,
                                           grid=(0, 1),
                                           tipText=tipText)
        row += 1
        div = LabelDivider(frameB, text='Isotopomers', grid=(row, 0))

        row += 1
        frame = Frame(frameB, grid=(row, 0))
        frame.expandGrid(1, 2)

        self.isotopomerFrame = frame
        self.abundanceWidget = MultiWidget(self,
                                           FloatEntry,
                                           relief='raised',
                                           borderwidth=2,
                                           callback=self.setDefaultAbundances,
                                           useImages=False)

        tipText = 'Opens a panel that allows you to set the basis/default abundances for C, H & N isotopes; used as the starting point for new isotopomer definitions'
        self.abundanceButton = Button(frame,
                                      text='Set Default\nAbundances',
                                      borderwidth=1,
                                      command=self.enterDefaultAbundances,
                                      grid=(0, 0),
                                      tipText=tipText)

        tipText = 'Sets the basis/default abundances for C, H & N isotopes to their natural abundance proportions'
        button = Button(frame,
                        text='Set Natural\nAbundance Default',
                        borderwidth=1,
                        command=self.resetDefaultAbundance,
                        grid=(0, 1),
                        sticky='ew',
                        tipText=tipText)

        label = Label(frame, text='Molecule Type:', grid=(0, 2), sticky='e')
        entries = standardResidueCcpCodes.keys()
        entries.sort()
        entries.reverse()
        tipText = 'Selects which type of bio-polymer to define residue isotopomer labelling for'
        self.molTypePulldown = PulldownList(frame,
                                            callback=self.setMolType,
                                            texts=entries,
                                            grid=(0, 3),
                                            tipText=tipText)

        row += 1
        tipTexts = [
            'The CCPN code that identifies the kind of residue the isotopomer relates to',
            'The number of the particular isotopomer (isotope pattern) within its residue type',
            'The fraction of the total residues, of its kind, that the isotopomer make up'
        ]
        headingList = ['Ccp Code', 'Variant', 'Weight']
        self.isotopomerWeightEntry = FloatEntry(
            self, text='', returnCallback=self.setIsotopomerWeight, width=6)
        editWidgets = [None, None, self.isotopomerWeightEntry]
        editGetCallbacks = [None, None, self.getIsotopomerWeight]
        editSetCallbacks = [None, None, self.setIsotopomerWeight]

        self.isotopomerMatrix = ScrolledMatrix(
            frameB,
            tipTexts=tipTexts,
            headingList=headingList,
            callback=self.selectIsotopomer,
            editWidgets=editWidgets,
            editSetCallbacks=editSetCallbacks,
            editGetCallbacks=editGetCallbacks,
            multiSelect=True,
            grid=(row, 0))
        self.isotopomerMatrix.doEditMarkExtraRules = self.isotopomerEditRules

        row += 1
        frame = Frame(frameB, grid=(row, 0), sticky='ew')
        frame.expandGrid(0, 0)

        tipTexts = [
            'Delete the selected residue isotopomers from the current isotope scheme',
            'Make a new residue isotopomer definition by copying the details of the last selected isotopomer',
            'Add a complete set of isotopomers to the isotope scheme, one for each residue type, based on the states default isotope abundances',
            'For all residue isotopomers in the scheme, set the labelling of one kind of atom (the user is prompted) to its default isotopic incorporation ',
            'Add a new residue isotopomer definition that uses the default isotopic incorporation'
        ]

        texts = [
            'Delete\nSelected', 'Copy\nSelected', 'Add Default\nAbundance Set',
            'Set Atom Type\nTo Default', 'Add\nNew:'
        ]

        commands = [
            self.removeIsotopomers, self.duplicateResidues,
            self.addDefaultIsotopomers, self.setAtomTypeDefault,
            self.addNewIsotopomer
        ]

        self.isotopomerButtons = ButtonList(frame,
                                            texts=texts,
                                            commands=commands,
                                            grid=(0, 0),
                                            tipTexts=tipTexts)
        tipText = 'Selects which kind of residue isotopomer may be added to the current isotope scheme'
        self.ccpCodePulldown = PulldownList(frame,
                                            callback=None,
                                            grid=(0, 1),
                                            sticky='e',
                                            tipText=tipText)

        row += 1
        div = LabelDivider(frameB, text='Atom Labels', grid=(row, 0))

        row += 1
        frame = Frame(frameB, grid=(row, 0))
        frame.expandGrid(1, 3)

        label = Label(frame, text='Chemical Element:', grid=(0, 0))
        tipText = 'Selects which kind of atoms to select from the selected residue isotopomer; to display isotopic incorporation in the below table'
        self.elementPulldown = PulldownList(frame,
                                            callback=self.changeChemElement,
                                            grid=(0, 1),
                                            tipText=tipText)
        self.updateChemElements()

        label = Label(frame, text='Water Exchangeable Atoms:', grid=(0, 2))
        tipText = 'Sets whether to show atoms considered as being "water exchangeable"; their isotopic labelling will rapidly equilibrate with aqueous solvent'
        self.exchangeCheck = CheckButton(frame,
                                         callback=self.updateAtomLabelsAfter,
                                         grid=(0, 3),
                                         selected=False,
                                         tipText=tipText)
        row += 1
        # Tip texts set on update
        headingList = [
            'Atom\nName', 'Weighting\n13C'
            'Weighting\n12C', '%12C', '%13C'
        ]
        self.atomLabelTupleWeightEntry = FloatEntry(
            self, text='', width=6, returnCallback=self.setAtomLabelWeight)

        self.atomsMatrix = ScrolledMatrix(frameB,
                                          headingList=headingList,
                                          callback=self.selectAtomLabel,
                                          multiSelect=True,
                                          grid=(row, 0))
        self.atomsMatrix.doEditMarkExtraRules = self.atomsEditRules

        row += 1
        tipTexts = [
            'For the selected atom sites, in the current isotopomer, set their isotopic incorporation to the default values',
            'Spread the isotopic incorporation values from the last selected atom site to all selected atoms sites'
        ]
        texts = ['Reset Selected to Default Abundance', 'Propagate Abundances']
        commands = [self.setAtomLabelsDefault, self.propagateAbundances]
        self.atomButtons = ButtonList(frameB,
                                      texts=texts,
                                      commands=commands,
                                      grid=(row, 0),
                                      tipTexts=tipTexts)

        #
        # View Frame
        #

        frameC.expandGrid(1, 0)

        row = 0
        frame = Frame(frameC, grid=(row, 0), sticky='ew')
        frame.grid_columnconfigure(3, weight=1)

        label = Label(frame, text='Residue Type:', grid=(0, 0))
        tipText = 'Selects which kind of residue, within the current isotope scheme, to show isotopomer structures for'
        self.viewCcpCodePulldown = PulldownList(
            frame,
            callback=self.selectViewCcpcode,
            grid=(0, 1),
            tipText=tipText)

        label = Label(frame, text='Isotopomer:', grid=(0, 2))
        tipText = 'Selects which kind of isotopomer (labelling pattern) to display, from the selected residue type.'
        self.viewIsotopomerPulldown = PulldownList(
            frame,
            callback=self.selectViewIsotopomer,
            grid=(0, 3),
            tipText=tipText)

        row += 1
        self.viewIsotopomerFrame = ViewIsotopomerFrame(frameC,
                                                       None,
                                                       grid=(row, 0))

        #
        # Main
        #

        tipTexts = [
            'Save all changes to the reference isotope scheme to disk; the saves ALL changes to the CCPN installation for all projects to use',
        ]
        texts = ['Save Schemes']
        commands = [self.saveSchemes]
        self.bottomButtons = UtilityButtonList(tabbedFrame.sideFrame,
                                               texts=texts,
                                               commands=commands,
                                               helpUrl=self.help_url,
                                               grid=(0, 0),
                                               sticky='e',
                                               tipTexts=tipTexts)

        self.updateChemElements()
        self.updateCcpCodes()
        self.updateSchemes()
        self.administerNotifiers(self.registerNotify)

    def atomsEditRules(self, atomLabel, row, col):

        if self.scheme:
            return isSchemeEditable(self.scheme)

        else:
            return False

    def isotopomerEditRules(self, isotopomer, row, col):

        if self.scheme:
            return isSchemeEditable(self.scheme)

        else:
            return False

    def schemeEditRules(self, scheme, row, col):

        return isSchemeEditable(scheme)

    def administerNotifiers(self, notifyFunc):

        for func in ('__init__', 'delete', 'setLongName', 'setDetails'):
            for clazz in ('ccp.molecule.ChemCompLabel.LabelingScheme', ):
                notifyFunc(self.updateSchemes, clazz, func)

        for func in ('__init__', 'delete', 'setWeight'):
            notifyFunc(self.updateIsotopomersAfter,
                       'ccp.molecule.ChemCompLabel.Isotopomer', func)
            notifyFunc(self.updateAtomLabelsAfter,
                       'ccp.molecule.ChemCompLabel.AtomLabel', func)

    def getCcpCodeIsotopomers(self, ccpCode):

        chemCompLabel = self.scheme.findFirstChemCompLabel(
            molType=self.molType, ccpCode=ccpCode)

        if chemCompLabel:
            isotopomers = list(chemCompLabel.isotopomers)

        else:
            isotopomers = []

        return isotopomers

    def selectViewCcpcode(self, ccpCode):

        if ccpCode != self.ccpCodeV:
            self.ccpCodeV = ccpCode
            self.isotopomerV = False
            self.updateViewIsotopomerPulldown()

    def selectViewIsotopomer(self, isotopomer):

        self.isotopomerV = isotopomer

        if isotopomer is None:
            isotopomers = self.getCcpCodeIsotopomers(self.ccpCodeV)
        else:
            isotopomers = [
                isotopomer,
            ]

        self.viewIsotopomerFrame.setIsotopomers(isotopomers)

    def updateViewCcpCodePulldown(self):

        if self.scheme:
            codes = self.getCcpCodes(self.molType)

            if self.ccpCodeV not in codes:
                self.ccpCodeV = codes[0]
                self.isotopomerV = False  # Not None
                self.updateViewIsotopomerPulldown()

            index = codes.index(self.ccpCodeV)

        else:
            codes = []
            index = 0

        self.viewCcpCodePulldown.setup(codes, codes, index)

    def updateViewIsotopomerPulldown(self):

        index = 0
        isotopomers = []
        names = []

        if self.scheme:
            isotopomers = self.getCcpCodeIsotopomers(self.ccpCodeV)
            names = ['%d' % i.serial for i in isotopomers]

            isotopomers.insert(0, None)
            names.insert(0, '<All>')

            if self.isotopomerV not in isotopomers:
                self.isotopomerV = None
                isotopomers = self.getCcpCodeIsotopomers(self.ccpCodeV)
                self.viewIsotopomerFrame.setIsotopomers(isotopomers)

        self.viewIsotopomerPulldown.setup(names, isotopomers, index)

    def updateButtons(self):

        buttonsA = self.schemeButtons.buttons
        buttonsB = self.isotopomerButtons.buttons
        buttonsC = self.atomButtons.buttons

        if self.scheme:
            buttonsA[0].enable()
            isEditable = isSchemeEditable(self.scheme)

            if isEditable:
                buttonsA[1].enable()
            else:
                buttonsA[1].disable()

            buttonsB[2].enable()
            buttonsB[3].enable()
            self.bottomButtons.buttons[0].enable()

            if isEditable:
                if self.isotopomer:
                    for button in buttonsB:
                        button.enable()
                    for button in buttonsC:
                        button.enable()

                else:
                    buttonsB[0].disable()
                    buttonsB[1].disable()
                    buttonsB[3].disable()
                    buttonsC[0].disable()
                    buttonsC[1].disable()

                    buttonsB[2].enable()
                    buttonsB[4].enable()

            else:
                for button in buttonsB:
                    button.disable()
                for button in buttonsC:
                    button.disable()

        else:
            buttonsA[0].disable()
            buttonsA[1].disable()
            for button in buttonsB:
                button.disable()
            for button in buttonsC:
                button.disable()
            self.bottomButtons.buttons[0].disable()

    def resetDefaultAbundance(self):

        self.defaultAbun = {}

    def setDefaultAbundances(self, values):

        self.abundanceWidget.place_forget()

        if values is not None:
            i = 0
            for element in elementSymbols:  # TBD getAllIsotopes
                for code, isotope in getSortedIsotopes(self.project, element):
                    self.defaultAbun[isotope] = values[i]
                    i += 1

    def enterDefaultAbundances(self):

        x = self.isotopomerFrame.winfo_x()
        y = self.isotopomerFrame.winfo_y()
        x0 = self.abundanceButton.winfo_x()
        y0 = self.abundanceButton.winfo_y()

        values = []
        options = []

        for element in elementSymbols:  # TBD getAllIsotopes
            for code, isotope in getSortedIsotopes(self.project, element):
                options.append(code + ':')
                values.append(
                    self.defaultAbun.get(isotope, 100.0 * isotope.abundance))

        N = len(values)
        self.abundanceWidget.maxRows = N
        self.abundanceWidget.minRows = N
        self.abundanceWidget.set(values=values, options=options)
        self.abundanceWidget.place(x=x + x0, y=y + y0)

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

        self.atomLabelTuple = (obj, col)

    def setMolType(self, molType):

        if molType != self.molType:
            self.molType = molType
            self.isotopomer = None
            self.updateCcpCodes()
            self.updateIsotopomers()

    def getCcpCodes(self, molType):

        codes = []
        for code in standardResidueCcpCodes[molType]:
            codes.append(code)

        codes.sort()

        return codes

    def updateCcpCodes(self):

        codes = self.getCcpCodes(self.molType)

        if self.isotopomer:
            index = codes.index(self.isotopomer.ccpCode)
        else:
            index = 0

        self.ccpCodePulldown.setup(codes, codes, index)

    def setIsotopomerWeight(self, event):

        value = self.isotopomerWeightEntry.get()

        if value is not None:
            self.isotopomer.setWeight(abs(value))

    def getIsotopomerWeight(self, isotopomer):

        if isotopomer:
            self.isotopomerWeightEntry.set(isotopomer.weight)

    def setSchemeName(self, event):

        text = self.schemeNameEntry.get()

        if text:
            text = text.strip() or None
        else:
            text = None

        self.scheme.setLongName(text)

    def getSchemeName(self, scheme):

        if scheme:
            self.schemeNameEntry.set(scheme.longName)

    def getSchemeDetails(self, scheme):

        if scheme:
            self.schemeDetailsEntry.set(scheme.details)

    def setSchemeDetails(self, event):

        text = self.schemeDetailsEntry.get()

        if text:
            text = text.strip() or None
        else:
            text = None

        self.scheme.setDetails(text)

    def updateSchemes(self, obj=None):

        textMatrix = []
        objectList = []

        for labelingScheme in self.project.sortedLabelingSchemes():

            repository = labelingScheme.findFirstActiveRepository()

            if repository:
                saveLocation = repository.name
            else:
                saveLocation = None

            line = [
                labelingScheme.name, labelingScheme.longName,
                labelingScheme.details, saveLocation
            ]

            textMatrix.append(line)
            objectList.append(labelingScheme)

        self.schemeMatrix.update(textMatrix=textMatrix, objectList=objectList)

        self.updateSchemePulldown()
        self.updateIsotopomers()

    def updateSchemePulldown(self):

        scheme = self.scheme
        schemes = self.project.sortedLabelingSchemes()
        names = [ls.longName or ls.name for ls in schemes]

        if names:
            if scheme not in schemes:
                scheme = schemes[0]

            index = schemes.index(scheme)

        else:
            index = 0
            scheme = None

        self.setLabellingScheme(scheme)
        self.schemePulldown.setup(names, schemes, index)

    def copyScheme(self):

        if self.scheme:
            name = askString('Input text', 'New Scheme Code:', '', parent=self)
            scheme = self.project.findFirstLabelingScheme(name=name)
            if scheme:
                showWarning('Failure', 'Scheme name already in use')
                return

            if name:
                newScheme = copySubTree(self.scheme,
                                        self.project,
                                        topObjectParameters={'name': name})

            else:
                showWarning('Failure', 'No name specified')

        else:
            showWarning('Failure', 'No scheme selected to copy')

    def removeScheme(self):

        if self.scheme and isSchemeEditable(self.scheme):
            self.scheme.delete()
            self.scheme = None

    def makeNewScheme(self):

        name = askString('Input text', 'New Scheme Code:', '', parent=self)

        if name:
            scheme = self.project.findFirstLabelingScheme(name=name)
            if scheme:
                showWarning('Failure', 'Scheme name already in use')
            else:
                scheme = self.project.newLabelingScheme(name=name)
                self.scheme = scheme

        else:
            showWarning('Failure', 'No name specified')

    def setLabellingScheme(self, scheme):

        if scheme is not self.scheme:
            self.scheme = scheme
            self.isotopomerV = False
            self.isotopomer = None
            self.updateIsotopomers()

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

        self.setLabellingScheme(object)
        self.updateSchemePulldown()

    def open(self):

        BasePopup.open(self)

        self.updateSchemes()

    def saveSchemes(self):

        schemes = [x for x in self.project.labelingSchemes if x.isModified]

        if schemes:
            for scheme in schemes:
                scheme.save()
            showInfo('Notice', 'Successfully saved %d schemes' % len(schemes))
            self.updateSchemes()

        else:
            showWarning('Notice', 'No modified schemes to save')

    def addNewIsotopomer(self):

        if self.scheme:
            ccpCode = self.ccpCodePulldown.getObject()
            chemCompLabel = self.getChemCompLabel(self.molType, ccpCode)

            self.makeIsotopomer(chemCompLabel)

    def makeIsotopomer(self, chemCompLabel, weight=1.0):

        isotopomer = chemCompLabel.newIsotopomer(weight=weight)

        chemComp = chemCompLabel.chemComp

        for chemAtom in chemComp.chemAtoms:
            if chemAtom.elementSymbol:
                chemElement = chemAtom.chemElement
                for isotope in chemElement.isotopes:
                    code = '%d%s' % (isotope.massNumber,
                                     chemAtom.elementSymbol)
                    weight = self.defaultAbun.get(isotope,
                                                  100.0 * isotope.abundance)
                    isotopomer.newAtomLabel(name=chemAtom.name,
                                            subType=chemAtom.subType,
                                            isotopeCode=code,
                                            weight=weight)

        return isotopomer

    def getChemCompLabel(self, molType, ccpCode):

        chemCompLabel = None
        if self.scheme:
            chemCompLabel = self.scheme.findFirstChemCompLabel(molType=molType,
                                                               ccpCode=ccpCode)
            if not chemCompLabel:
                chemCompLabel = self.scheme.newChemCompLabel(molType=molType,
                                                             ccpCode=ccpCode)
        return chemCompLabel

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

        self.isotopomer = obj
        self.updateChemElements()
        self.updateAtomLabels()

    def updateIsotopomersAfter(self, obj=None):

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

    def updateIsotopomers(self):

        self.updateViewCcpCodePulldown()
        self.updateViewIsotopomerPulldown()

        textMatrix = []
        objectList = []

        if self.scheme:
            chemCompLabels = [(label.ccpCode, label)
                              for label in self.scheme.chemCompLabels]
            chemCompLabels.sort()

            for key, chemCompLabel in chemCompLabels:
                if chemCompLabel.molType == self.molType:
                    for isotopomer in chemCompLabel.sortedIsotopomers():

                        line = [
                            chemCompLabel.ccpCode, isotopomer.serial,
                            isotopomer.weight
                        ]

                        textMatrix.append(line)
                        objectList.append(isotopomer)

        self.isotopomerMatrix.update(textMatrix=textMatrix,
                                     objectList=objectList)
        self.updateAtomLabelsAfter()
        self.waiting = False

    def setAtomTypeDefault(self):

        if self.scheme:
            atomName = askString(
                'Query',
                'Specify atom name to set\ndefault abundance for',
                'H',
                parent=self)

            if not atomName:
                return

            atomLabels = []
            for chemCompLabel in self.scheme.chemCompLabels:
                if chemCompLabel.molType == self.molType:
                    for isotopomer in chemCompLabel.isotopomers:
                        # Multiple because of isotopes and subTypes
                        atomLabels += isotopomer.findAllAtomLabels(
                            name=atomName)

            if atomLabels:
                for atomLabel in atomLabels:
                    isotope = atomLabel.isotope
                    weight = self.defaultAbun.get(isotope,
                                                  100.0 * isotope.abundance)
                    atomLabel.weight = weight

            else:
                data = (atomName, self.scheme.name)
                msg = 'Atom name %s does not match any atoms in %s scheme isotopomers' % data
                showWarning('Failure', msg)

    def addDefaultIsotopomers(self):

        if self.scheme:
            codes = self.getCcpCodes(self.molType)

            for ccpCode in codes:
                chemCompLabel = self.getChemCompLabel(self.molType, ccpCode)

                if not chemCompLabel.isotopomers:
                    self.makeIsotopomer(chemCompLabel)

    def removeIsotopomers(self):

        isotopomers = self.isotopomerMatrix.currentObjects

        if isotopomers:
            for isotopomer in isotopomers:
                isotopomer.delete()

            self.isotopomer = None

    def duplicateResidues(self):

        isotopomers = self.isotopomerMatrix.currentObjects

        for isotopomer in isotopomers:
            chemCompLabel = isotopomer.chemCompLabel
            new = copySubTree(isotopomer, chemCompLabel)

    def updateChemElements(self):

        if self.isotopomer:
            chemCompLabel = self.isotopomer.chemCompLabel
            elementDict = {}

            for chemAtom in chemCompLabel.chemComp.chemAtoms:
                symbol = chemAtom.elementSymbol
                if symbol:
                    elementDict[symbol] = True

            names = elementDict.keys()
            names.sort()

        else:
            names = ['C', 'N', 'H']

        if self.element not in names:
            index = 0
        else:
            index = names.index(self.element)

        self.elementPulldown.setup(names, names, index)

    def updateAtomLabelsAfter(self, obj=None):

        if self.waitingAtom:
            return

        else:
            self.waitingAtom = True
            self.after_idle(self.updateAtomLabels)

    def updateAtomLabels(self):

        element = self.elementPulldown.getText()

        textMatrix = []
        objectList = []
        headingList = [
            'Atom Name',
        ]
        tipTexts = [
            'The name of the atom within its residue, for which to set isotopic abundances, within the selected isotopomer',
        ]

        isotopeCodes = [x[0] for x in getSortedIsotopes(self.project, element)]
        headingList.extend(['Weighting\n%s' % x for x in isotopeCodes])
        tip = 'The amount of %s isotope incorporation; can be a ratio, percentage or fraction (the value used is relative to the sum of all weights)'
        tipTexts.extend([tip % x for x in isotopeCodes])

        tip = 'The percentage %s isotope incorporation, calculated using stated weights'
        headingList.extend(['%%%s' % x for x in isotopeCodes])
        tipTexts.extend([tip % x for x in isotopeCodes])

        editWidgets = [
            None,
        ]
        editGetCallbacks = [
            None,
        ]
        editSetCallbacks = [
            None,
        ]
        editWidgets.extend(
            [self.atomLabelTupleWeightEntry for x in isotopeCodes])
        editGetCallbacks.extend(
            [self.getAtomLabelWeight for x in isotopeCodes])
        editSetCallbacks.extend(
            [self.setAtomLabelWeight for x in isotopeCodes])
        editWidgets.extend([None for x in isotopeCodes])
        editGetCallbacks.extend([None for x in isotopeCodes])
        editSetCallbacks.extend([None for x in isotopeCodes])

        doExchangeable = self.exchangeCheck.get()

        if self.isotopomer:

            atomDict = {}
            for atomLabel in self.isotopomer.sortedAtomLabels():
                if atomLabel.chemAtom.elementSymbol == element:
                    if (not doExchangeable) and (
                            atomLabel.chemAtom.waterExchangeable):
                        continue

                    name = atomLabel.name
                    subType = atomLabel.subType
                    atomDict[(name, subType)] = True

            atomNames = atomDict.keys()
            atomNames = greekSortAtomNames(atomNames)

            for atomName, subType in atomNames:

                if subType == 1:
                    name = atomName
                else:
                    name = '%s:%d' % (atomName, subType)

                line = [
                    name,
                ]
                atomLabels = []

                sumWeights = 0.0
                for isotope in isotopeCodes:
                    atomLabel = self.isotopomer.findFirstAtomLabel(
                        name=atomName, subType=subType, isotopeCode=isotope)
                    atomLabels.append(atomLabel)

                    if atomLabel:
                        weight = atomLabel.weight
                        sumWeights += weight
                        line.append(weight)

                    else:
                        line.append(0.0)

                if sumWeights:
                    for atomLabel in atomLabels:
                        line.append(100.0 * atomLabel.weight / sumWeights)

                else:
                    for atomLabel in atomLabels:
                        line.append(None)

                textMatrix.append(line)
                objectList.append(atomLabels)

        self.atomsMatrix.update(textMatrix=textMatrix,
                                objectList=objectList,
                                headingList=headingList,
                                tipTexts=tipTexts,
                                editWidgets=editWidgets,
                                editGetCallbacks=editGetCallbacks,
                                editSetCallbacks=editSetCallbacks)

        self.waitingAtom = False
        self.updateButtons()

    def setAtomLabelWeight(self, event):

        value = self.atomLabelTupleWeightEntry.get()

        if value is not None:
            atomLabels, col = self.atomLabelTuple

            chemAtom = None
            for label in atomLabels:
                if label:
                    chemAtom = label.chemAtom
                    break

            atomLabel = atomLabels[col - 1]

            if chemAtom and (atomLabel is None):
                isotopeCode, isotope = getSortedIsotopes(
                    self.project, chemAtom.elementSymbol)[col - 1]
                atomLabel = self.isotopomer.newAtomLabel(
                    name=chemAtom.name,
                    subType=chemAtom.subType,
                    isotopeCode=isotopeCode,
                    weight=value)
            atomLabel.setWeight(value)

    def setAtomLabelsDefault(self):

        atomLabelTuples = self.atomsMatrix.currentObjects

        for atomLabels in atomLabelTuples:
            for atomLabel in atomLabels:
                isotope = atomLabel.isotope
                weight = self.defaultAbun.get(isotope,
                                              100.0 * isotope.abundance)

                atomLabel.weight = weight

    def propagateAbundances(self):

        atomLabels, col = self.atomLabelTuple
        atomLabelTuples = self.atomsMatrix.currentObjects

        weightDict = {}
        for atomLabel in atomLabels:
            weightDict[atomLabel.isotope] = atomLabel.weight

        for atomLabels0 in atomLabelTuples:
            if atomLabels0 != atomLabels:
                for atomLabel in atomLabels0:
                    atomLabel.weight = weightDict[atomLabel.isotope]

    def getAtomLabelWeight(self, null):

        atomLabels, col = self.atomLabelTuple

        if atomLabels and (col > 0):
            atomLabel = atomLabels[col - 1]

            if atomLabel is None:
                weight = 0.0
            else:
                weight = atomLabel.weight

            self.atomLabelTupleWeightEntry.set(weight)

    def changeChemElement(self, name):

        self.element = name
        self.isotopes = [x[0] for x in getSortedIsotopes(self.project, name)]
        self.updateAtomLabels()

    def destroy(self):

        self.administerNotifiers(self.unregisterNotify)

        BasePopup.destroy(self)
Ejemplo n.º 6
0
class BrowsePeakGroupsPopup(BasePopup):
  """
  **Display Groups of Peaks Linked by a Root Assignment**
  
  This popup window is used to display the results of operations in Analysis that
  generate grouped peaks, usually linked by virtue of a common 'root' assignment.
  For example a selected set of peaks may be macthed to other peaks with
  similar positions in a spectrum dimension, where each group corresponds
  to a separate C-H or N-H position. For example the right-click
  window menu option "Locate Peaks::Match multiple peaks" generates
  such groupings to find rows or columns of peaks that share chemical shifts.
  
  Whatever operation generated the groups, they are listed in ranked order in the
  table; best scoring at the top. The number of peaks in the group and any common
  assigment and position is also listed. The idea is that the groups are candidates
  for some searh or macthing process that the user triggered. The best scoring
  groups may be used to control the spectrum window display, thus displaying any
  peak matches, by using [Display Groups in Strips] after selecting  an approprate
  window. This system is often used for NOESY experiments where resonances that are
  close in space share common sets of peak positions; they are close to the same
  set of other resonances.

  """
  
  def __init__(self, parent, *args, **kw):
    
    self.groups         = []
    self.variableDim    = 1
    self.windowPane     = None
    self.rulers         = []
    self.peakLists      = []
    self.orthogonalDict = {}
    self.parent         = parent
    self.searchPeaks    = []

    BasePopup.__init__(self, parent, title= "Peak Groups", borderwidth=5, **kw)
    
  def body(self, guiFrame):

    guiFrame.grid_rowconfigure(0, weight=1)
    guiFrame.grid_columnconfigure(0, weight=1)
  
    row = 0 
    frame = LabelFrame(guiFrame, text='Matched Peak Groups')
    frame.grid_rowconfigure(0, weight=1)
    frame.grid_columnconfigure(0, weight=1)
    frame.grid(row=row, sticky='nsew')
    
    headingList, tipTexts = self.getHeadingList()
    self.scrolledMatrix = ScrolledMatrix(frame, initialRows=15, multiSelect=True, 
                                        headingList=headingList, tipTexts=tipTexts,
                                        grid=(0,0), gridSpan=(1,3))
 
    tipTexts = ['Remove the selected peak groups from the table',
                'Show 1D positional ruler lines for the selected groups',
                'Remove any ruler lines previously added for peak group',
                'Display selected peak groups within strips of selected window']
    texts = ['Remove\nGroups','Show\nRulers',
             'Delete\nRulers','Display Groups\nIn Strips']
    commands = [self.removeGroups,self.addRulers,
                self.removeRulers,self.stripGroups]
    self.buttons = ButtonList(frame, texts=texts, commands=commands, tipTexts=tipTexts)
    self.buttons.grid(row=1, column=0, sticky='ew')
    
    tipText = 'Selects the spectrum window in which to display strips & ruler lines'
    label = Label(frame, text='Target window:', grid=(1,1))
    self.windowPulldown = PulldownList(frame, callback=None,
                                       grid=(1,2), tipText=tipText)
    
    row+= 1
    bottomButtons = UtilityButtonList(guiFrame, helpUrl=self.help_url, expands=True, doClone=False)
    bottomButtons.grid(row = row, sticky = 'ew')

    self.update()

    for func in ('__init__', 'delete', 'setName'):
      self.registerNotify(self.updateWindowsAfter, 'ccpnmr.Analysis.SpectrumWindow', func)

  def open(self):
  
    self.update()
    BasePopup.open(self)
    
  def updateWindowsAfter(self, opt=None):
 
    self.after_idle(self.updateWindows)

  def updateWindows(self):
    
    
    if not self.groups:
      self.windowPulldown.clear()
      return

    selected = self.windowPulldown.getText()
    groups   = self.scrolledMatrix.currentObjects or self.groups
    self.orthogonalDict = {}
    windowPane = self.windowPane
    if windowPane and groups:

      meanVarPos = 0.0
      for peak in groups[0][2]:
        meanVarPos += peak.sortedPeakDims()[self.variableDim].value
      meanVarPos /= float(len(groups[0][2]))
     
      xyz = []
      for score, coords0, peaks in groups:
      
        peak       = peaks[0]
        peakDims   = peak.sortedPeakDims()
        dimMapping = getPeakDimAxisMapping(peak, self.windowPane)

        coords     = list(coords0)
        coords.insert(self.variableDim, meanVarPos)
        
        posDict = {}
        for i, peakDim in enumerate(peakDims):
          posDict[peakDim] = coords[i] 

        pos = []
        for axisPanel in windowPane.sortedAxisPanels():
          peakDim = dimMapping[axisPanel.label]
          pos.append( posDict[peakDim] )
          
        xyz.append(pos)
      
      windowZPlanes = findOrthogonalWindows(windowPane, xyz,
                                            excludeQuery=False)
      for windowPosition in windowZPlanes:
        if windowPosition:
          key, pane, xyz = windowPosition
          self.orthogonalDict[key] = (pane, xyz)
 
    xyPlaneKeys = self.orthogonalDict.keys()
    xyPlaneKeys.sort()
    if xyPlaneKeys:
      index = min(self.windowPulldown.index, len(xyPlaneKeys)-1)

      if selected in xyPlaneKeys:
        index = xyPlaneKeys.index(selected)
    
      self.windowPulldown.setup(xyPlaneKeys, xyPlaneKeys, index)

     
  def removeGroups(self):
  
    groups = self.scrolledMatrix.currentObjects
    
    if groups:
            
      newGroups = []
      for group in self.groups:
        if group not in groups:
          newGroups.append(group)
      
      self.update(groups=newGroups)
  
  def stripGroups(self):

    self.updateWindows()
    name = self.windowPulldown.getText()
    groups = self.scrolledMatrix.currentObjects
    
    if groups and name and (name != '<None>'):
      selected = self.windowPulldown.getText()
      windowPane, positions = self.orthogonalDict[selected]
      window = windowPane.spectrumWindow

      N = len(positions)
      msg = '%d positions selected. Really make %d strips in window %s'
      if N > 10 and not showOkCancel('Warning', msg % (N,N,window.name ), parent=self):
        return

      displayStrips(self.parent, positions, orthoPositions=None,
                    spectrum=None, windowPane=windowPane)
      # often fails first time...
      self.update_idletasks()
      displayStrips(self.parent, positions, orthoPositions=None,
                    spectrum=None, windowPane=windowPane)

  
  def removeRulers(self):
  
    for ruler in self.rulers:
      if not ruler.isDeleted:
        ruler.delete()
    
    self.rulers = []
  
  def addRulers(self):
     
    groups = self.scrolledMatrix.currentObjects
    if groups and self.windowPane:
      
      positions  = []
      panelTypes = []
      for peak in self.searchPeaks:
        peakDim    = peak.sortedPeakDims()[self.variableDim]
        dimMapping = getPeakDimAxisMapping(peak, self.windowPane)
        for axisPanel in self.windowPane.axisPanels:
          if dimMapping[axisPanel.label] is peakDim:
            positions.append(peakDim.value)
            panelTypes.append(axisPanel.panelType)
            break

      color = '#808080'
      for i in range(len(positions)):
        ruler = createRuler(positions[i], panelTypes[i],
                            dashLength=3, gapLength=1,
                            color=color, remove=False)
        self.rulers.append(ruler)


  def update(self, groups=None, variableDim=None, 
             peakLists=None, searchPeaks=None):
  
    self.removeRulers()

    if groups is not None:
      self.groups = groups
    
    if variableDim is not None:
      self.variableDim = variableDim

    if peakLists:
      self.peakLists = peakLists
      spectrum = self.peakLists[0].dataSource
      for spectrumWindow in self.analysisProject.spectrumWindows:
        for windowPane in spectrumWindow.spectrumWindowPanes:
          if isSpectrumInWindowPane(windowPane, spectrum):
            if len(windowPane.axisPanels) == len(spectrum.dataDims):
              self.windowPane = windowPane
              break

    self.searchPeaks = searchPeaks or []

    objectList = []
    textMatrix = []
    for i, group in enumerate(self.groups):
      
      (score,positions,peaks) = group
      datum = ['%d' % (i+1),
               '%f' % score,
               '%d' % len(peaks)]
      
      annotations = []
      for j in range(len(peaks[0].peakDims)):
        if j != self.variableDim:
          annotation = ''
          
          for peak in peaks:
            peakDims = peak.sortedPeakDims()
            peakDim = peakDims[j]
            
            if peakDim.annotation:
              if annotation and (annotation != peakDim.annotation):
                annotation = '*'
              else:
                annotation = peakDim.annotation
          annotations.append(annotation)
     
      datum.append(' '.join(annotations))
      
      for position in positions:
        datum.append('%f' % position)
     
      objectList.append(group)
      textMatrix.append(datum)

    headingList, tipTexts = self.getHeadingList()
    self.scrolledMatrix.update(textMatrix=textMatrix,
                               objectList=objectList,
                               headingList=headingList,
                               tipTexts=tipTexts)
    self.updateWindows()

  def getHeadingList(self):
  
    tipTexts = ['The ranking of the peak group, comparing its score with others',
                'The match score of the peak group',
                'Number of peaks within the matched peak group',
                'The root (usually amide) assignment, common to peaks in a group']

    headingList = ['Rank','Score','Num\npeaks','Root\nAssignment']
    
    if self.groups:
      n = len( self.groups[0][2][0].peakDims )
      
      for i in range(n):
        if i != self.variableDim:
          headingList.append( 'F%d position' % (i+1) )
          tipTexts.append('Average location of the group in the F%d dimension' % (i+1))
          
    else:
      headingList.extend(['F1 position','F2 position'])
      tipTexts.append('Average location of the group in the F1 dimension')
      tipTexts.append('Average location of the group in the F2 dimension')
    
    return headingList, tipTexts
    
  def destroy(self):
  
    for func in ('__init__', 'delete', 'setName'):
      self.unregisterNotify(self.updateWindowsAfter, 'ccpnmr.Analysis.SpectrumWindow', func)
    
    BasePopup.destroy(self)
Ejemplo n.º 7
0
class OpenSpectrumPopup(BasePopup):
    r"""
  **Locate Spectrum Data for Use in CCPN Project**
  
  This popup window enables the user to locate spectrum data within a file
  system and associate the files (typically binary) with an experiment and
  spectrum name so that it may be visualised and accessed within the current
  CCPN project. Spectra of many different origins and file formats may be
  loaded, which currently includes Bruker, Varian, Felix, NMRPipe, NmrView,
  SPARKY/UCSF, Azara and the factorised shape format "USF3". Depending upon the
  file format of the spectrum, data loaded the user may be required to either
  select a parameter file which then refers to the actual spectrum intensity
  data; this is true for Bruker "procs" and AZARA ".par" files, or alternatively
  a spectrum data file itself that contains referencing information; this is
  the case for SPARKY/UCSF, NmrView and NMRPipe spectra.

  The layout of the popup involved two sections; the upper of which is for
  navigating to and selecting the spectrum or parameter files within the
  file-system, and the lower is for specifying how each spectrum is loaded into
  the CCPN project. It should be noted that when spectrum parameters are read
  the first time, the relevant information is copied into the CCPN project,
  where it may be adjusted independently of the original file information. No
  copies of the spectrum intensity data are made, the CCPN project merely refers
  to the spectrum data on disk, although the data file for a loaded spectrum may
  subsequently be moved or replaced.

  In normal operation the user first selects the kind of spectrum file format
  that will be loaded via the upper "File format" pulldown menu and then checks
  that the "File type" pulldown (toward the bottom of the file browser) is set
  to detect the appropriate kinds of filename; if a helpful file name filter is
  not available the user can add one via the "Manual Select" field, taking care
  to add any wild-card symbols, like the asterisk in "\*.ft3". Next the spectrum
  data or parameter files, appropriate to the selected format, are located by
  navigating within the file-system browser. When the required spectrum files are
  visible the user selects one *or more* to load. Multiple file selections may
  be made using left-click with <Ctrl> (toggle selection) or <Shift> (select
  range). It should be noted that when selecting Bruker files, when using the
  standard Bruker directory structure, the user only needs to navigate to the
  numbered spectrum directory; by default the "procs" file two levels down is
  searched for, e.g. "123/pdata/1/procs" is shown in the directory containing
  the "123" directory.

  When spectrum or parameter files are selected in the file table, the lower
  "Spectra To Open" table is filled to reflect the selection. The user should
  then be mindful of the settings within this table and may choose to edit
  various things by double-clicking on the appropriate cell. Typically the user
  just adjusts the name of the independent "Experiment" and "Spectrum" records.
  These names are usually concatenated like "expName:specName" in CCPN graphical
  displays so there is no need to repeat a name in both fields; this only takes
  up more space. The Experiment, which is a record of *what was done
  experimentally*, commonly has a short name like "HNCA" or "HSQC_298K" so the
  user readily knows how to interpret the experimental data. The Spectrum, which
  is a record of *the data that was collected*, commonly has a short name to
  identify the spectrum number or file name. An Experiment record may contain
  several Spectrum records, so the spectrum's name need minimally only identify
  it amongst others from the same experiment. The Shift List value may be
  changed if the user knows that the experiment represents a distinct set of
  conditions, with different spectrum peak/resonance positions, to existing or
  other experiments being entered. Each shift list will be curated separately,
  to give separate chemical shift values for assignments made under different
  conditions (even when relating to the same atoms). The shift list that an
  experiment uses may also be changed at any time after loading.

  When all spectra and options are specified the [Open Spectrum] button will
  load the relevant data into the CCPN project. If the "Skip verification
  dialogs" option is set it is assumed that all of the spectrum point to
  frequency referencing information, and any data file references, are correct.
  Otherwise, the user will be prompted to confirm the file details and
  referencing information for each spectrum in turn. Finally, after loading the
  user is asked to set the type of NMR experiment, in terms of general
  magnetisation transfer pathway, that was performed.

  **Caveats & Tips**

  If the name of an Experiment that is *already within the CCPN project* is
  used, then the loaded spectrum will (assuming it is compatible) be entered
  under that existing experiment record; no new experiment entity will be
  defined. The user may legitimately use this feature to load several spectra
  that relate to the same experiment; typically where spectra are different
  projections. To facilitate this the "Use shared experiment" option can be
  selected.

  Although experiments and spectra may be renamed after loading, a spectrum
  record may not be placed under a different experiment once created; deletion
  and re-loading is the only mans of achieving this, and care must be taken in
  transferring any assignments.

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

        self.experiment = None
        self.currentObject = None
        #self.currentObjects = [] # looks obsolete

        BasePopup.__init__(self,
                           parent=parent,
                           title='Experiment : Open Spectra',
                           **kw)

    def open(self):

        self.message()
        BasePopup.open(self)

    def body(self, guiFrame):

        self.fileSelect = None
        names, objects = self.getShiftLists()
        self.shiftListPulldown = PulldownList(self,
                                              callback=self.setShiftList,
                                              texts=names,
                                              objects=objects)
        self.windowPulldown = PulldownList(self,
                                           texts=WINDOW_OPTS,
                                           callback=self.setWindow)
        self.experimentEntry = Entry(self,
                                     width=16,
                                     returnCallback=self.setExperiment)
        self.spectrumEntry = Entry(self,
                                   width=16,
                                   returnCallback=self.setSpectrum)

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

        leftFrame = LabelFrame(guiFrame, text='File Selection')
        leftFrame.grid(row=0, column=0, sticky='nsew')
        leftFrame.grid_columnconfigure(3, weight=1)

        row = 0

        label = Label(leftFrame, text='File format:')
        label.grid(row=row, column=0, sticky='w')
        tipText = 'Selects which kind of spectrum file is being loaded; what its data matrix format is'
        self.formatPulldown = PulldownList(leftFrame,
                                           callback=self.chooseFormat,
                                           texts=file_formats,
                                           tipText=tipText,
                                           grid=(row, 1))

        self.detailsLabel = Label(leftFrame, text='Show details:')
        tipText = 'Whether to show an annotation that describes the spectrum in the file selection; currently only uses comment fields from Bruker spectra'
        self.detailsSelect = CheckButton(leftFrame,
                                         selected=False,
                                         callback=self.showDetails,
                                         tipText=tipText)
        self.titleRow = row
        self.detailsSelected = False

        row = row + 1
        leftFrame.grid_rowconfigure(row, weight=1)
        file_types = [FileType('All', ['*'])]
        self.fileSelect = FileSelect(leftFrame,
                                     multiSelect=True,
                                     file_types=file_types,
                                     single_callback=self.chooseFiles,
                                     extraHeadings=('Details', ),
                                     extraJustifies=('left', ),
                                     displayExtra=False,
                                     getExtraCell=self.getDetails,
                                     manualFileFilter=True)
        self.fileSelect.grid(row=row, column=0, columnspan=6, sticky='nsew')

        rightFrame = LabelFrame(guiFrame, text='Spectra To Open')
        rightFrame.grid(row=1, column=0, sticky='nsew')
        rightFrame.grid_columnconfigure(3, weight=1)

        row = 0
        label = Label(rightFrame,
                      text='Skip verification dialogs:',
                      grid=(row, 0))
        tipText = 'Whether to allow the user to check file interpretation and referencing information before the spectrum is loaded'
        self.verifySelect = CheckButton(rightFrame,
                                        selected=False,
                                        grid=(row, 1),
                                        tipText=tipText)

        label = Label(rightFrame, text='Use shared experiment:', grid=(row, 2))
        tipText = 'When selecting multiple spectrum files, whether the loaded spectra will all belong to (derive from) the same experiment; useful for projection spectra etc.'
        self.sharedExpSelect = CheckButton(rightFrame,
                                           selected=False,
                                           tipText=tipText,
                                           callback=self.useShared,
                                           grid=(row, 3))

        row = row + 1
        rightFrame.grid_rowconfigure(row, weight=1)
        tipTexts = [
            'A short textual name for the experiment record that the loaded spectrum will belong to; may be a new experiment or the name of an existing one',
            'A short textual name to identify the spectrum within its experiment; typically a few characters or spectrum number, rather than a repeat of the experiment name',
            'The location of the file, relative to the current directory, that the spectrum data will be loaded from',
            'Sets which window or windows the spectrum will initially appear within once loaded',
            'Sets which shift list the experiment (and hence loaded spectrum) will use to curate chemical shift information; can be changed after load time'
        ]
        headingList = [
            'Experiment', 'Spectrum', 'File', 'Windows', 'Shift List'
        ]
        editWidgets = [
            self.experimentEntry, self.spectrumEntry, None,
            self.windowPulldown, self.shiftListPulldown
        ]
        editGetCallbacks = [
            self.getExperiment, self.getSpectrum, None, self.getWindow,
            self.getShiftList
        ]
        editSetCallbacks = [
            self.setExperiment, self.setSpectrum, None, self.setWindow,
            self.setShiftList
        ]
        self.scrolledMatrix = ScrolledMatrix(rightFrame,
                                             headingList=headingList,
                                             callback=self.selectCell,
                                             editWidgets=editWidgets,
                                             multiSelect=True,
                                             editGetCallbacks=editGetCallbacks,
                                             editSetCallbacks=editSetCallbacks,
                                             tipTexts=tipTexts,
                                             grid=(row, 0),
                                             gridSpan=(1, 4))

        row = row + 1
        tipTexts = [
            'Load spectrum or spectra into the CCPN project using the selected file(s)',
        ]
        texts = ['Open Spectrum']
        commands = [self.openSpectra]
        bottomButtons = UtilityButtonList(guiFrame,
                                          texts=texts,
                                          tipTexts=tipTexts,
                                          doClone=False,
                                          commands=commands,
                                          helpUrl=self.help_url)
        bottomButtons.grid(row=row, column=0, columnspan=1, sticky='ew')
        self.openButton = bottomButtons.buttons[0]

        self.chooseFormat('Azara')
        self.message()

    def message(self):

        if not self.project or len(self.nmrProject.experiments) < 1:
            pass
            #self.parent.ticker.setMessage('Choose spectrum files to open....     ')

    def showDetails(self, isSelected):

        self.detailsSelected = isSelected
        self.fileSelect.updateDisplayExtra(isSelected)
        # below is so that when Details column is toggled on it will actually
        # be seen without having to use the scrollbar
        self.fileSelect.fileList.refreshSize()

    def useShared(self, isSelected):

        self.chooseFiles(forceUpdate=True)

        #if isSelected:

        #objects = self.scrolledMatrix.objectList
        #if len(objects) > 1:
        #  self.currentObject = objects[0]
        #  text = objects[0][0]
        #  self.chooseFiles()
        #  for oo in objects[1:]:
        #    oo[0] = text
        #  if self.project:
        #    self.experiment = self.nmrProject.findFirstExperiment(name=text)
        #self.update()

    def gridDetails(self, bool):

        if bool:
            self.detailsLabel.grid(row=self.titleRow, column=2, sticky='w')
            self.detailsSelect.grid(row=self.titleRow, column=3, sticky='w')
            self.fileSelect.updateDisplayExtra(self.detailsSelected)
        else:
            self.detailsLabel.grid_forget()
            self.detailsSelect.grid_forget()
            self.fileSelect.updateDisplayExtra(False)

    def openSpectra(self):

        noVerify = self.verifySelect.getSelected()

        # tracks if 'add to existing experiment' has already ben OK'ed
        self.okExpSet = set()

        directory = self.fileSelect.getDirectory()
        spectra = []
        specIndex = 0
        for obj in self.scrolledMatrix.objectList:
            fileName = uniIo.joinPath(directory, obj.fileName)
            spectrum = self.openSpectrum(obj.exptName, obj.specName, fileName,
                                         obj.window, obj.shiftListName)
            specIndex += 1

            if (spectrum):
                # check endianness if we are not verifying
                spectra.append(spectrum)
                if noVerify:
                    isBigEndian = isSpectrumBigEndian(
                        spectrum)  # according to data in file
                    if isBigEndian is not None:
                        isBigEndianCurr = getIsSpectrumBigEndian(
                            spectrum)  # according to data model
                        setIsSpectrumBigEndian(spectrum, isBigEndian)
                        if isBigEndian != isBigEndianCurr:
                            if isBigEndian:
                                s = 'big'
                            else:
                                s = 'little'
                            print 'WARNING: swapped endianess of spectrum to %s endian' % s
        #
        del self.okExpSet

        if noVerify and len(spectra) > 1 and self.sharedExpSelect.getSelected(
        ):
            # if we are using a shared experiment and not verifying,
            # set referencing to match first spectrum for all

            # get reference spectrum and set up data structure
            # use most recent pre-existing spectrum, otherwise first new one
            refSpec = spectra[0]
            for spec in spectra[0].experiment.sortedDataSources():
                if spec in spectra:
                    break
                else:
                    refSpec = spec

            ddrLists = {}
            refDdrs = []
            for dataDim in refSpec.sortedDataDims():
                for ddr in dataDim.dataDimRefs:
                    ddrLists[ddr.expDimRef] = []
                    refDdrs.append(ddr)

            # get dataDimRefs, store by ExpDimRef,
            # checking that all spectra have data dim refs for same set of xdr
            nTotal = len(ddrLists)
            for spec in spectra:
                nFound = 0
                for dataDim in spec.sortedDataDims():
                    for ddr in dataDim.dataDimRefs:
                        xdr = ddr.expDimRef
                        ll = ddrLists.get(xdr)
                        if ll is None:
                            # something did not match - do nothing
                            break
                        else:
                            ll.append(ddr)
                            nFound += 1
                else:
                    if nFound == nTotal:
                        # we are OK. Do next spectrum
                        continue
                # something did not match - do nothing
                break
            else:

                # all spectra matched. Now reset O1 references to match reference
                if refSpec is spectra[0]:
                    startAt = 1
                else:
                    startAt = 0

                for refDdr in refDdrs:
                    dataDim = refDdr.dataDim
                    centrePoint = dataDim.numPointsOrig / 2 - dataDim.pointOffset + 1
                    refValue = refDdr.pointToValue(centrePoint)

                    xdr = refDdr.expDimRef
                    for ddr in ddrLists[xdr][startAt:]:
                        dataDim = ddr.dataDim
                        centrePoint = dataDim.numPointsOrig / 2 - dataDim.pointOffset + 1
                        ddr.refPoint = centrePoint
                        ddr.refValue = refValue

        # set refExperiment if there is only one possibility
        experiments = []
        ignoreSet = set()
        showPopup = False
        for spectrum in spectra:
            experiment = spectrum.experiment
            if experiment not in ignoreSet:
                ignoreSet.add(experiment)
                if not experiment.refExperiment:
                    experiments.append(spectrum.experiment)
                    if noVerify:
                        resetCategory = False
                        if not hasattr(experiment, 'category'):
                            if (hasattr(experiment, 'pulProgName')
                                    and hasattr(experiment, 'pulProgType')):
                                # this is first time we get here, and we have external name and source
                                # use external source to set fullType
                                experiment.category = 'use external'
                                resetCategory = True
                        refExperiments = getRefExperiments(experiment)
                        if resetCategory and not refExperiments:
                            # no refExperiments match external source.
                            # unset 'use external' category
                            del experiment.category

                        if len(refExperiments) == 1:
                            # only one possibility, just set it
                            setRefExperiment(experiment, refExperiments[0])

                        # wb104: 20 Oct 2014: do not popup Experiment types dialog if noVerify
                        #else:
                        #  showPopup = True

        # Pop up refExperiment verification
        if experiments and (showPopup or not noVerify):
            self.parent.initRefExperiments(experiments)

        # set up internal Analysis data
        for spectrum in spectra:
            self.parent.finishInitSpectrum(spectrum)
            print 'finished opening spectrum', spectrum.experiment.name, spectrum.name

    def chooseFiles(self, forceUpdate=False, *file):

        directory = self.fileSelect.getDirectory()

        fileNames = self.fileSelect.fileList.currentObjects
        fullFileNames1 = [uniIo.joinPath(directory, x) for x in fileNames]

        fullFileNames2 = [x.fileName for x in self.scrolledMatrix.objectList]
        fullFileNames2 = [uniIo.joinPath(directory, x) for x in fullFileNames2]

        if fullFileNames1 == fullFileNames2 and not forceUpdate:
            return

        objectList = []
        textMatrix = []

        format = self.formatPulldown.getText()

        shiftListName = self.getShiftLists()[0][0]
        windowOpt = WINDOW_OPTS[1]
        oneUp = os.path.dirname

        if format == 'Bruker':
            if self.sharedExpSelect.getSelected():
                nameTemplate = 'Bruker_%d'
                next = self.getNextExpNum(nfiles=len(fileNames),
                                          nameTemplate=nameTemplate)

                exptName = nameTemplate % (next)
                for i, fileName in enumerate(fileNames):
                    fullFileName = fullFileNames1[i]
                    specName = os.path.basename(
                        oneUp(oneUp(oneUp(fullFileName))))
                    datum = (exptName, specName, fileName, windowOpt,
                             shiftListName)
                    dataObj = RowObject(*datum)
                    textMatrix.append(datum)
                    objectList.append(dataObj)

            else:
                for i, fileName in enumerate(fileNames):
                    fullFileName = fullFileNames1[i]
                    try:  # below should not fail
                        ss1 = oneUp(fullFileName)
                        specName = os.path.basename(ss1)
                        ss2 = os.path.basename(oneUp(oneUp(ss1)))
                        exptName = 'Bruker_' + ss2
                    except:  # just put in something
                        ss = os.path.basename(fullFileName)
                        exptName = 'Bruker_' + ss
                        specName = ss

                    datum = (exptName, specName, fileName, windowOpt,
                             shiftListName)
                    dataObj = RowObject(*datum)
                    textMatrix.append(datum)
                    objectList.append(dataObj)

        else:
            next = self.getNextExpNum(nfiles=len(fileNames))
            if self.sharedExpSelect.getSelected():
                exptName = 'Expt_%d' % (next)
                for i, fileName in enumerate(fileNames):
                    specName = re.sub('\.\w+$', '', fileName)

                    datum = (exptName, specName, fileName, windowOpt,
                             shiftListName)
                    dataObj = RowObject(*datum)
                    textMatrix.append(datum)
                    objectList.append(dataObj)

            else:
                for i, fileName in enumerate(fileNames):
                    exptName = 'Expt_%d' % (next + i)
                    specName = re.sub('\.\w+$', '', fileName)

                    datum = (exptName, specName, fileName, windowOpt,
                             shiftListName)
                    dataObj = RowObject(*datum)
                    textMatrix.append(datum)
                    objectList.append(dataObj)

        if len(fileNames) > 1:
            self.openButton.config(text='Open Spectra')

        else:
            self.openButton.config(text='Open Spectrum')

        self.scrolledMatrix.update(objectList=objectList,
                                   textMatrix=textMatrix)

    def getNextExpNum(self, nfiles=0, nameTemplate='Expt_%d'):
        """ get suitable free integer to use for exp names 
    """
        next = 1
        if self.project:
            nmrProject = self.nmrProject
            ii = len(nmrProject.experiments)

            # find first exp number that is not taken
            # NBNB TBD could consider expname = specname, specname = proc dir
            next = ii + 1
            if nfiles:
                while ii < next + nfiles:
                    ii += 1
                    if nmrProject.findFirstExperiment(name=nameTemplate % ii):
                        next = ii + 1
        #
        return next

    def getDetails(self, fullfile):

        details = ''
        if os.path.isfile(fullfile):
            format = self.formatPulldown.getText()
            detailsDir = os.path.dirname(fullfile)
            detailsFile = uniIo.joinPath(detailsDir, details_file_dict[format])
            if os.path.exists(detailsFile):
                fp = open(detailsFile)
                details = fp.read().strip().replace('\n',
                                                    ' ').replace('\r', ' ')
                fp.close()

        return (details, )

    def update(self):

        objectList = self.scrolledMatrix.objectList
        textMatrix = [(obj.exptName, obj.specName, obj.fileName, obj.window,
                       obj.shiftListName) for obj in objectList]

        self.scrolledMatrix.update(objectList=objectList,
                                   textMatrix=textMatrix)

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

        self.currentObject = obj

        if self.project:
            self.experiment = self.nmrProject.findFirstExperiment(
                name=obj.exptName)
        else:
            self.experiment = None

    def getWindow(self, obj):

        if obj:
            self.windowPulldown.set(obj.window)

    def setWindow(self, opt):

        if isinstance(opt, RowObject):
            self.currentObject.window = opt.window
        else:
            self.currentObject.window = opt
        self.update()

    def setShiftList(self, obj=None):

        if self.project:
            project = self.project
            shiftList = self.shiftListPulldown.getObject()
            if shiftList is None:
                shiftList = newShiftList(project, unit='ppm')

            if self.experiment and shiftList and (
                    shiftList is not self.experiment.shiftList):
                setExperimentShiftList(self.experiment, shiftList)

            self.currentObject.shiftListName = shiftList.name

        self.update()

    def getShiftList(self, object):

        names, shiftLists = self.getShiftLists()
        if names:
            self.shiftListPulldown.setup(names, shiftLists, 0)
            if self.experiment and self.experiment.shiftList:
                name = self.experiment.shiftList.name
            else:
                name = object.shiftListName

            if name is not None:
                self.shiftListPulldown.set(name)

    def getShiftLists(self):

        if self.project:
            names = []
            objects = getShiftLists(self.nmrProject)
            for shiftList in objects:
                if not shiftList.name:
                    shiftList.name = 'ShiftList %d' % shiftList.serial
                names.append(shiftList.name)

            objects.append(None)
            names.append('<New>')

        else:
            objects = [
                None,
            ]
            names = [
                'ShiftList 1',
            ]

        return names, objects

    def chooseFormat(self, format):

        if format in ('Bruker', 'Varian'):
            self.gridDetails(True)
        else:
            self.gridDetails(False)

        file_types = []
        file_type = file_type_dict.get(format)
        if (file_type):
            file_types.extend([file_type])
        file_types.append(FileType('All', ['*']))
        file_types.append(self.fileSelect.manualFilter)
        self.fileSelect.setFileTypes(file_types)

    def getSpectrum(self, obj):

        if obj:
            self.spectrumEntry.set(obj.specName)

    def setSpectrum(self, *event):

        if self.currentObject:
            text = self.spectrumEntry.get()
            if text and text != ' ':
                for data in self.scrolledMatrix.objectList:
                    if data is self.currentObject:
                        continue
                    if (data.specName
                            == text) and (data.exptName
                                          == self.currentObject.exptName):
                        showWarning(
                            'Repeated name',
                            'Spectrum name (%s) already in use for experiment (%s)'
                            % (data.specName, data.exptName),
                            parent=self)
                        return
                    elif (self.experiment) and (
                            self.experiment.findFirstDataSource(name=text)):
                        showWarning(
                            'Repeated name',
                            'Spectrum name (%s) already in use for experiment (%s)'
                            % (data.specName, data.exptName),
                            parent=self)
                        return

                self.currentObject.specName = text
            self.update()

    def getExperiment(self, obj):

        if obj:
            self.experimentEntry.set(obj.exptName)

    def setExperiment(self, *event):

        if self.currentObject:
            text = self.experimentEntry.get()
            if text and text != ' ':
                if self.sharedExpSelect.getSelected():
                    # share one experiment for all rows
                    for oo in self.scrolledMatrix.objectList:
                        oo.exptName = text
                else:
                    #separate experiments
                    self.currentObject.exptName = text
                if self.project:
                    self.experiment = self.nmrProject.findFirstExperiment(
                        name=text)
            self.update()

    def updateShiftLists(self):

        if self.project:
            name = self.expt_entry.get()
            e = self.nmrProject.findFirstExperiment(name=name)

        else:
            e = None

        names, objects = self.getShiftLists()
        if e and e.shiftList:
            index = objects.index(e.shiftList)
        else:
            index = 0

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

    def openSpectrum(self,
                     exptName,
                     specName,
                     file,
                     windowOpt=WINDOW_OPTS[2],
                     shiftListName='<New>',
                     extraData=None):

        # input checks
        if not file:
            showError('No file', 'Need to enter file', parent=self)
            return None

        if not exptName:
            showError('Experiment',
                      'Need to enter experiment name',
                      parent=self)
            return None

        if not specName:
            showError('Spectrum', 'Need to enter spectrum name', parent=self)
            return None

        # get or set up project
        project = self.project
        if not project:
            self.project = project = defaultProject()
            self.parent.initProject(project)
            self.nmrProject = self.parent.nmrProject
            self.analysisProject = self.parent.analysisProject
            #Default ShiftList with name 'ShiftList 1' created

        # set up shift list
        if shiftListName == '<New>':
            shiftList = None
        else:
            shiftList = self.nmrProject.findFirstMeasurementList(
                className='ShiftList', name=shiftListName)

        # read params

        format = self.formatPulldown.getText()
        clazz = params_class_dict[format]
        try:
            params = clazz(file, extraData=extraData)
        except Implementation.ApiError, e:
            showError('Reading params file',
                      'Fatal error: ' + e.error_msg,
                      parent=self)
            return None

        dim = params.pseudoDataDim()
        if dim is not None:
            if format == 'NMRPipe':
                popup = NmrPipePseudoPopup(self, params, dim, file)
                popup.destroy()
            elif format == 'Bruker':
                popup = BrukerPseudoPopup(self, params, dim)
                popup.destroy()

        # get or set up experiment

        experiment = self.nmrProject.findFirstExperiment(name=exptName)
        if experiment:
            expIsNew = False
            if experiment.findFirstDataSource(name=specName):
                showError('Duplicate name',
                          'Duplicate spectrum name "%s" in experiment %s' %
                          (specName, experiment.name),
                          parent=self)
                return None
            elif (experiment.dataSources and experiment not in self.okExpSet):
                if showOkCancel('Multiple Spectra Warning',
                                'Really put multiple '
                                'spectra into existing experiment %s?' %
                                experiment.name,
                                parent=self):
                    self.okExpSet.add(experiment)
                else:
                    return

        else:
            expIsNew = True
            try:
                # Will also work for shiftList == None
                experiment = Nmr.Experiment(self.nmrProject,
                                            name=exptName,
                                            numDim=params.ndim,
                                            shiftList=shiftList)

            except Implementation.ApiError, experiment:
                showError('Experiment', experiment.error_msg, parent=self)
                return None
Ejemplo n.º 8
0
class PrintFrame(LabelFrame):
    def __init__(self,
                 parent,
                 getOption=None,
                 setOption=None,
                 text='Print Options',
                 haveTicks=False,
                 doOutlineBox=True,
                 *args,
                 **kw):

        self.getOption = getOption
        self.setOption = setOption
        self.haveTicks = haveTicks
        self.doOutlineBox = doOutlineBox

        LabelFrame.__init__(self, parent=parent, text=text, *args, **kw)

        self.file_select_popup = None

        self.getOptionValues()

        try:
            size_index = self.paper_types.index(self.paper_type)
        except:
            size_index = 0

        try:
            other_unit_index = self.paper_units.index(self.other_unit)
        except:
            other_unit_index = 0

        try:
            orientation_index = self.paper_orientations.index(self.orientation)
        except:
            orientation_index = 0

        try:
            style_index = self.style_choices.index(self.output_style)
        except:
            style_index = 0

        try:
            format_index = self.format_choices.index(self.output_format)
        except:
            format_index = 0

        if haveTicks:
            try:
                tick_location_index = self.tick_locations.index(
                    self.tick_location)
            except:
                tick_location_index = 0

        self.grid_columnconfigure(2, weight=1)

        row = 0
        label = Label(self, text='File:')
        label.grid(row=row, column=0, sticky='e')
        self.file_entry = Entry(self, width=40, text=self.file_name)
        self.file_entry.grid(row=row, column=1, columnspan=2, sticky='ew')
        button = Button(self, text='Choose File', command=self.findFile)
        button.grid(row=row, column=3, rowspan=2, sticky='nsew')

        row += 1
        label = Label(self, text='Title:')
        label.grid(row=row, column=0, sticky='e')
        self.title_entry = Entry(self, width=40, text=self.title)
        self.title_entry.grid(row=row, column=1, columnspan=2, sticky='ew')

        row += 1
        frame = Frame(self)
        frame.grid(row=row, column=0, columnspan=4, sticky='ew')
        frame.grid_columnconfigure(4, weight=1)

        label = Label(frame, text='Paper size:')
        label.grid(row=0, column=0, sticky='e')
        entries = []
        for t in paper_types:
            if t == Output.other_paper_type:
                entry = t
            else:
                (w, h, u) = paper_sizes[t]
                entry = t + ' (%2.1f %s x %2.1f %s)' % (w, u, h, u)
            entries.append(entry)
        self.size_menu = PulldownList(frame,
                                      callback=self.changedSize,
                                      texts=entries,
                                      index=size_index)
        self.size_menu.grid(row=0, column=1, sticky='w')

        self.other_frame = Frame(frame)

        self.other_frame.grid_columnconfigure(0, weight=1)
        self.other_entry = FloatEntry(self.other_frame,
                                      text=self.other_size,
                                      isArray=True)
        self.other_entry.grid(row=0, column=0, sticky='ew')
        self.other_unit_menu = PulldownList(self.other_frame,
                                            texts=paper_units,
                                            index=other_unit_index)
        self.other_unit_menu.grid(row=0, column=1, sticky='ew')

        row += 1
        frame = Frame(self)
        frame.grid(row=row, column=0, columnspan=4, sticky='ew')
        frame.grid_columnconfigure(1, weight=1)
        frame.grid_columnconfigure(3, weight=1)
        frame.grid_columnconfigure(5, weight=1)

        label = Label(frame, text='Orientation:')
        label.grid(row=0, column=0, sticky='e')
        self.orientation_menu = PulldownList(frame,
                                             texts=paper_orientations,
                                             index=orientation_index)
        self.orientation_menu.grid(row=0, column=1, sticky='w')

        label = Label(frame, text='  Style:')
        label.grid(row=0, column=2, sticky='e')
        self.style_menu = PulldownList(frame,
                                       texts=style_choices,
                                       index=style_index)
        self.style_menu.grid(row=0, column=3, sticky='w')

        label = Label(frame, text='  Format:')
        label.grid(row=0, column=4, sticky='e')
        self.format_menu = PulldownList(frame,
                                        callback=self.changedFormat,
                                        texts=format_choices,
                                        index=format_index)

        self.format_menu.grid(row=0, column=5, sticky='w')

        if haveTicks:

            row += 1
            frame = Frame(self)
            frame.grid(row=row, column=0, columnspan=4, sticky='ew')
            frame.grid_columnconfigure(1, weight=1)
            frame.grid_columnconfigure(3, weight=1)

            label = Label(frame, text='Tick Location:')
            label.grid(row=0, column=0, sticky='e')
            self.tick_menu = PulldownList(frame,
                                          texts=tick_locations,
                                          index=tick_location_index)
            self.tick_menu.grid(row=0, column=1, sticky='w')

            label = Label(frame, text='  Tick Placement:')
            label.grid(row=0, column=2, sticky='e')
            self.tick_buttons = CheckButtons(frame,
                                             entries=tick_placements,
                                             selected=self.tick_placement)
            self.tick_buttons.grid(row=0, column=3, sticky='w')

        row += 1
        frame = Frame(self)
        frame.grid(row=row, column=0, columnspan=4, sticky='ew')
        frame.grid_columnconfigure(3, weight=1)

        label = Label(frame, text='Include:')
        label.grid(row=0, column=0, sticky='e')
        self.border_buttons = CheckButtons(frame,
                                           entries=border_decorations,
                                           selected=self.border_decoration)
        self.border_buttons.grid(row=0, column=1, sticky='w')

        label = Label(frame, text='  Scaling:')
        label.grid(row=0, column=2, sticky='e')
        self.scaling_scale = Scale(frame,
                                   orient=Tkinter.HORIZONTAL,
                                   value=self.scaling)
        self.scaling_scale.grid(row=0, column=3, sticky='ew')

    def destroy(self):

        self.setOptionValues()

        if self.file_select_popup:
            self.file_select_popup.destroy()

        Frame.destroy(self)

    def getOptionValues(self):

        getOption = self.getOption
        if getOption:

            file_name = getOption('FileName', defaultValue='')
            title = getOption('Title', defaultValue='')
            paper_type = getOption('PaperSize', defaultValue=paper_types[0])
            paper_type = paper_type_dict.get(paper_type, paper_types[0])
            other_height = getOption('OtherHeight', defaultValue=10)
            other_width = getOption('OtherWidth', defaultValue=10)
            other_size = [other_height, other_width]
            other_unit = getOption('OtherUnit', defaultValue=paper_units[0])
            orientation = getOption('Orientation',
                                    defaultValue=paper_orientations[0])
            in_color = getOption('InColor', defaultValue=True)
            if in_color:
                output_style = style_choices[0]
            else:
                output_style = style_choices[1]
            format_option = getOption('OutputFormat',
                                      defaultValue=format_options[0])
            output_format = format_choices[format_options.index(format_option)]
            if self.haveTicks:
                tick_outside = getOption('TickOutside',
                                         defaultValue=tick_locations[0])
                if tick_outside:
                    tick_location = tick_locations.index(PrintTicks.Outside)
                else:
                    tick_location = tick_locations.index(PrintTicks.Inside)
                tick_placement = getTickPlacement1(
                    getOption('TickPlacement', defaultValue='nsew'))
            dateTime = getOption('ShowsDateTime', defaultValue=True)
            fileName = getOption('ShowsFileName', defaultValue=True)
            border_decoration = []
            if dateTime:
                border_decoration.append(border_decorations[0])
            if fileName:
                border_decoration.append(border_decorations[1])
            scaling = getOption('Scaling', defaultValue=0.9)
            scaling = int(round(100.0 * scaling))

        else:

            file_name = ''
            title = ''
            paper_type = paper_types[0]
            other_unit = paper_units[0]
            other_size = ''
            orientation = paper_orientations[0]
            output_style = style_choices[0]
            output_format = format_choices[0]
            if self.haveTicks:
                tick_location = tick_locations[0]
                tick_placement = tick_placements
            border_decoration = border_decorations
            scaling = 90

        if not self.haveTicks:
            tick_location = None
            tick_placement = None

        self.file_name = file_name
        self.title = title
        self.paper_type = paper_type
        self.other_unit = other_unit
        self.other_size = other_size
        self.orientation = orientation
        self.output_style = output_style
        self.output_format = output_format
        self.tick_location = tick_location
        self.tick_placement = tick_placement
        self.border_decoration = border_decoration
        self.scaling = scaling

    def setOptionValues(self):

        self.file_name = file_name = self.file_entry.get()
        self.title = title = self.title_entry.get()

        n = self.size_menu.getSelectedIndex()
        self.paper_type = paper_type = paper_types[n]
        if paper_type == Output.other_paper_type:
            other_size = self.other_entry.get()
            other_unit = self.other_unit_menu.getText()
        else:
            other_size = None
            other_unit = None
        self.other_size = other_size
        self.other_unit = other_unit

        self.paper_orientation = paper_orientation = self.orientation_menu.getText(
        )
        self.output_style = output_style = self.style_menu.getText()
        self.output_format = output_format = self.format_menu.getText()

        if self.haveTicks:
            tick_location = self.tick_menu.getText()
            tick_placement = self.tick_buttons.getSelected()
        else:
            tick_location = tick_placement = None
        self.tick_location = tick_location
        self.tick_placement = tick_placement

        self.border_decoration = border_decoration = self.border_buttons.getSelected(
        )
        scaling = self.scaling_scale.get()
        self.scaling = scaling = int(round(scaling))

        setOption = self.setOption
        if setOption:
            setOption('FileName', value=file_name)
            setOption('Title', value=title)
            if paper_type == Output.other_paper_type:
                setOption('OtherHeight', value=other_size[0])
                setOption('OtherWidth', value=other_size[1])
                setOption('OtherUnit', value=other_unit)
            else:
                paper_type = paper_type_inverse_dict[paper_type]
                setOption('PaperSize', value=paper_type)
            setOption('Orientation', value=paper_orientation)
            in_color = (output_style == style_choices[0])
            setOption('InColor', value=in_color)
            output_format = format_options[format_choices.index(output_format)]
            setOption('OutputFormat', value=output_format)
            if self.haveTicks:
                tick_outside = (tick_location == PrintTicks.Outside)
                setOption('TickOutside', value=tick_outside)
                tick_placement = getTickPlacement2(tick_placement)
                setOption('TickPlacement', value=tick_placement)
            dateTime = (border_decorations[0] in border_decoration)
            fileName = (border_decorations[1] in border_decoration)
            setOption('ShowsDateTime', value=dateTime)
            setOption('ShowsFileName', value=fileName)
            setOption('Scaling', value=0.01 * scaling)

    def findFile(self):

        if self.file_select_popup:
            self.file_select_popup.open()
        else:
            file_types = [
                FileType('All', ['*']),
                FileType('PostScript', ['*.ps', '*.eps']),
                FileType('PDF', ['*.pdf', '*.ai'])
            ]
            self.file_select_popup = FileSelectPopup(self,
                                                     file_types=file_types)

        file = self.file_select_popup.getFile()
        if file:
            self.file_entry.set(file)

    def changedSize(self, entry):

        if entry == Output.other_paper_type:
            self.other_frame.grid(row=0, column=2, columnspan=2, sticky='w')
        else:
            self.other_frame.grid_forget()

    def changedFormat(self, entry):

        file_suffix = file_suffixes.get(entry)
        if not file_suffix:
            return

        file_name = self.file_entry.get()
        if not file_name:
            return

        for suffix in format_suffixes:
            if file_name.endswith(suffix):
                if suffix != file_suffix:
                    n = len(suffix)
                    file_name = file_name[:-n] + file_suffix
                    self.file_entry.set(file_name)
                break
        else:
            file_name = file_name + file_suffix
            self.file_entry.set(file_name)

    # width and height are of plot, in pixels
    def getOutputHandler(self, width, height, fonts=None):

        if not fonts:
            fonts = []
        else:
            fonts = list(fonts)

        for n in range(len(fonts)):
            if fonts[n] == 'Times':
                fonts[n] = 'Times-Roman'

        self.setOptionValues()

        if not self.file_name:
            showError('No file', 'No file specified', parent=self)
            return None

        if os.path.exists(self.file_name):
            if not showYesNo('File exists',
                             'File "%s" exists, overwrite?' % self.file_name,
                             parent=self):
                return None

        if (self.paper_type == Output.other_paper_type):
            paper_size = self.other_size + [self.other_unit]
        else:
            paper_size = paper_sizes[self.paper_type]
        output_scaling = self.scaling / 100.0

        font = 'Times-Roman'
        border_text = {}
        for decoration in self.border_decoration:
            if (decoration == 'Time & date'):
                location = 'se'
                text = time.ctime(time.time())
            elif (decoration == 'File name'):
                location = 'sw'
                text = self.file_name
            else:
                continue  # should not be here
            border_text[location] = (text, font, 12)
        if (self.title):
            location = 'n'
            border_text[location] = (self.title, font, 18)

        if font not in fonts:
            fonts.append(font)

        outputHandler = PrintHandler.getOutputHandler(
            self.file_name,
            width,
            height,
            output_scaling=output_scaling,
            paper_size=paper_size,
            paper_orientation=self.paper_orientation,
            output_style=self.output_style,
            output_format=self.output_format,
            border_text=border_text,
            fonts=fonts,
            do_outline_box=self.doOutlineBox)

        return outputHandler

    def getAspectRatio(self):

        self.setOptionValues()

        if self.paper_type == Output.other_paper_type:
            paper_size = self.other_size
        else:
            paper_size = paper_sizes[self.paper_type]

        r = paper_size[1] / paper_size[0]
        if self.paper_orientation == 'Landscape':
            r = 1.0 / r

        return r
Ejemplo n.º 9
0
class PrintFrame(Frame):

  def __init__(self, parent, getOption = None, setOption = None,
               haveTicks = False, doOutlineBox = True, *args, **kw):

    self.getOption = getOption
    self.setOption = setOption
    self.haveTicks = haveTicks
    self.doOutlineBox = doOutlineBox

    Frame.__init__(self, parent=parent, *args, **kw)

    self.file_select_popup = None

    self.getOptionValues()

    try:
      size_index = paper_types.index(self.paper_type)
    except:
      size_index = 0

    try:
      other_unit_index = paper_units.index(self.other_unit)
    except:
      other_unit_index = 0

    try:
      orientation_index = paper_orientations.index(self.paper_orientation)
    except:
      orientation_index = 0

    try:
      style_index = style_choices.index(self.output_style)
    except:
      style_index = 0

    try:
      format_index = format_choices.index(self.output_format)
    except:
      format_index = 0

    if haveTicks:
      try:
        tick_location_index = tick_locations.index(self.tick_location)
      except:
        tick_location_index = 0

    self.grid_columnconfigure(1, weight=1)

    row = 0
    button = Button(self, text='File:', command=self.findFile,
                    tipText='Select location to save print file')
    button.grid(row=row, column=0, sticky='e')
    self.file_entry = Entry(self, width=40, text=self.file_name,
                tipText='Location where file is saved on disk')
    self.file_entry.grid(row=row, column=1, sticky='ew')

    row += 1
    label = Label(self, text='Title:')
    label.grid(row=row, column=0, sticky='e')
    self.title_entry = Entry(self, width=40, text=self.title,
                    tipText='Title of the printout, displayed at top')
    self.title_entry.grid(row=row, column=1, sticky='ew')

    row += 1
    label = Label(self, text='X axis label:')
    label.grid(row=row, column=0, sticky='e')
    self.x_axis_entry = Entry(self, width=40, text=self.x_axis_label,
                    tipText='X axis label for the printout')
    self.x_axis_entry.grid(row=row, column=1, sticky='ew')

    row += 1
    label = Label(self, text='Y axis label:')
    label.grid(row=row, column=0, sticky='e')
    self.y_axis_entry = Entry(self, width=40, text=self.y_axis_label,
                    tipText='Y axis label for the printout')
    self.y_axis_entry.grid(row=row, column=1, sticky='ew')

    row += 1
    frame = Frame(self)
    frame.grid(row=row, column=0, columnspan=2, sticky='ew')
    frame.grid_columnconfigure(4, weight=1)
    
    label = Label(frame, text='Paper size:')
    label.grid(row=0, column=0, sticky='e')
    entries = []
    for t in paper_types:
      if t == Output.other_paper_type:
        entry = t
      else:
        (w, h, u) = paper_sizes[t]
        entry = t + ' (%2.1f %s x %2.1f %s)' % (w, u, h, u)
      entries.append(entry)
    self.size_menu = PulldownList(frame, callback=self.changedSize,
                                  texts=entries, index=size_index,
                                  tipText='The paper size for the printout')
    self.size_menu.grid(row=0, column=1, sticky='w')

    self.other_frame = Frame(frame)

    self.other_frame.grid_columnconfigure(0, weight=1)
    self.other_entry = FloatEntry(self.other_frame, text=self.other_size,
                                  isArray=True,
                                  tipText='The size of the Other paper in both dimensions; this requires two values, space or comma separated')
    self.other_entry.grid(row=0, column=0, sticky='ew')
    self.other_unit_menu= PulldownList(self.other_frame, texts=paper_units,
                                       index=other_unit_index,
                                       tipText='The unit for the Other paper size')
    self.other_unit_menu.grid(row=0, column=1, sticky='ew')

    row += 1
    frame = Frame(self)
    frame.grid(row=row, column=0, columnspan=4, sticky='ew')
    frame.grid_columnconfigure(1, weight=1)
    frame.grid_columnconfigure(3, weight=1)
    frame.grid_columnconfigure(5, weight=1)

    label = Label(frame, text='Orientation:')
    label.grid(row=0, column=0, sticky='e')
    self.orientation_menu = PulldownList(frame, texts=paper_orientations,
                                         index=orientation_index,
                                         tipText='Whether the paper should be set in Portrait or Landscape mode')
    self.orientation_menu.grid(row=0, column=1, sticky='w')

    label = Label(frame, text='  Style:')
    label.grid(row=0, column=2, sticky='e')
    self.style_menu = PulldownList(frame, texts=style_choices,
                                   index=style_index,
                                   tipText='Whether the printout should be in colour or black and white')
    self.style_menu.grid(row=0, column=3, sticky='w')

    label = Label(frame, text='  Format:')
    label.grid(row=0, column=4, sticky='e')
    self.format_menu = PulldownList(frame, callback=self.changedFormat,
                                    texts=format_choices, index=format_index,
                                    tipText='Whether to save as PS, EPS or PDF')

    self.format_menu.grid(row=0, column=5, sticky='w')

    if haveTicks:

      row += 1
      frame = Frame(self)
      frame.grid(row=row, column=0, columnspan=4, sticky='ew')
      frame.grid_columnconfigure(1, weight=1)
      frame.grid_columnconfigure(3, weight=1)
      
      label = Label(frame, text='Tick Location:')
      label.grid(row=0, column=0, sticky='e')
      self.tick_menu = PulldownList(frame, texts=tick_locations,
                                    index=tick_location_index,
                                    tipText='Whether the tick marks appear on the inside or outside of the frame')
      self.tick_menu.grid(row=0, column=1, sticky='w')

      label = Label(frame, text='  Tick Placement:')
      label.grid(row=0, column=2, sticky='e')
      if self.tick_placement is None:
        selected = None
      else:
        selected = [(x in self.tick_placement) for x in tick_placements]
      self.tick_buttons = CheckButtons(frame, entries=tick_placements,
                                       selected=selected,
                                       tipTexts=('Whether the tick marks appear on the top and/or bottom and/or left and/or right',))
      self.tick_buttons.grid(row=0, column=3, sticky='w')

      row += 1
      frame = Frame(self)
      frame.grid(row=row, column=0, columnspan=4, sticky='ew')
      frame.grid_columnconfigure(1, weight=1)
      frame.grid_columnconfigure(3, weight=1)
      
      label = Label(frame, text='Tick Font:')
      label.grid(row=0, column=0, sticky='e')
      self.tick_font_list = FontList(frame, mode='Print',
                            selected=self.tick_font,
                            extraTexts=[PrintTicks.no_tick_text],
                            tipText='The font used for the tick mark labels')
      self.tick_font_list.grid(row=0, column=1, sticky='w')

      label = Label(frame, text='Tick Spacing:')
      label.grid(row=0, column=2, sticky='e')
      # TBD: put preferred choice in data model
      self.spacing_menu = PulldownList(frame, texts=spacing_choices, index=0,
                                       callback=self.changedSpacing,
                                       tipText='Whether the program should automatically calculate the major/minor tick spacings and how many decimal places are used for the ticks, or whether the these are specified manually')
      self.spacing_menu.grid(row=0, column=3, sticky='w')

      ff = self.spacing_frame = Frame(frame)
      ff.grid_columnconfigure(1, weight=1)
      ff.grid_columnconfigure(2, weight=1)
      label = Label(ff, text='Tick Spacing')
      label.grid(row=0, column=0, sticky='w')
      label = Label(ff, text='Major')
      label.grid(row=0, column=1, sticky='ew')
      label = Label(ff, text='Minor')
      label.grid(row=0, column=2, sticky='ew')
      label = Label(ff, text='Decimals')
      label.grid(row=0, column=3, sticky='ew')
      label = Label(ff, text='X:')
      label.grid(row=1, column=0, sticky='w')
      self.x_major_entry = FloatEntry(ff, tipText='The spacing in display units of the major tick marks in the X dimension')
      self.x_major_entry.grid(row=1, column=1, sticky='ew')
      self.x_minor_entry = FloatEntry(ff, tipText='The spacing in display units of the minor tick marks in the X dimension (not printed if left blank)')
      self.x_minor_entry.grid(row=1, column=2, sticky='ew')
      self.x_decimal_entry = IntEntry(ff, tipText='The number of decimal places for the tick numbers in the X dimension')
      self.x_decimal_entry.grid(row=1, column=3, sticky='ew')
      label = Label(ff, text='Y:')
      label.grid(row=2, column=0, sticky='w')
      self.y_major_entry = FloatEntry(ff, tipText='The spacing in display units of the major tick marks in the Y dimension')
      self.y_major_entry.grid(row=2, column=1, sticky='ew')
      self.y_minor_entry = FloatEntry(ff, tipText='The spacing in display units of the minor tick marks in the Y dimension (not printed if left blank)')
      self.y_minor_entry.grid(row=2, column=2, sticky='ew')
      self.y_decimal_entry = IntEntry(ff, tipText='The number of decimal places for the tick numbers in the Y dimension')
      self.y_decimal_entry.grid(row=2, column=3, sticky='ew')

      row += 1
      frame = Frame(self)
      frame.grid(row=row, column=0, columnspan=4, sticky='ew')
      frame.grid_columnconfigure(1, weight=1)
      
      label = Label(frame, text='Tick Length:')
      label.grid(row=0, column=0, sticky='e')
      # TBD: put preferred choice in data model
      self.tick_length_menu = PulldownList(frame, texts=tick_length_choices, index=0,
                                       callback=self.changedLength,
                                       tipText='Whether the program should automatically calculate the major/minor tick lengths, or whether the these are specified manually')
      self.tick_length_menu.grid(row=0, column=1, sticky='w')

      ff = self.length_frame = Frame(frame)
      ff.grid_columnconfigure(1, weight=1)
      label = Label(ff, text='  Major length:')
      label.grid(row=0, column=0, sticky='w')
      self.length_major_entry = FloatEntry(ff, tipText='The length in points of the major tick marks')
      self.length_major_entry.grid(row=0, column=1, sticky='w')
      label = Label(ff, text='Minor length:')
      label.grid(row=0, column=2, sticky='w')
      self.length_minor_entry = FloatEntry(ff, tipText='The length in points of the minor tick marks')
      self.length_minor_entry.grid(row=0, column=3, sticky='w')

    row += 1
    frame = Frame(self)
    frame.grid(row=row, column=0, columnspan=4, sticky='ew')
    frame.grid_columnconfigure(3, weight=1)
    frame.grid_columnconfigure(4, weight=1)

    label = Label(frame, text='Scaling:')
    label.grid(row=0, column=0, sticky='e')
    # TBD: put preferred choice in data model
    self.scaling_menu = PulldownList(frame, texts=scaling_choices, index=0,
                                     callback=self.changedScaling,
                                     tipText='Whether the plot should be scaled as a percentage of the maximum size that would fit on the paper, or instead should be specified by the number of cms or inches per unit')
    self.scaling_menu.grid(row=0, column=1, sticky='ew')

    self.scaling_scale = Scale(frame, orient=Tkinter.HORIZONTAL, value=self.scaling, tipText='The percentage of the maximum size that would fit on the paper that the plot is scaled by')
    self.scaling_scale.grid(row=0, column=2, columnspan=3, sticky='ew')

    self.x_scaling_label = Label(frame, text='X:')
    self.x_scaling_entry = FloatEntry(frame, tipText='The scaling that should be used in the X dimension as cms or inches per unit')
    self.y_scaling_label = Label(frame, text='Y:')
    self.y_scaling_entry = FloatEntry(frame, tipText='The scaling that should be used in the Y dimension as cms or inches per unit')

    row += 1
    frame = Frame(self)
    frame.grid(row=row, column=0, columnspan=4, sticky='w')
    frame.grid_columnconfigure(2, weight=1)
  
    label = Label(frame, text='Include:')
    label.grid(row=0, column=0, sticky='e')
    tipTexts = ('Whether the time and date should be included in the printout',
                'Whether the file name should be included in the printout')
    if self.border_decoration is None:
      selected = None
    else:
      selected = [(x in self.border_decoration) for x in border_decorations]
    self.border_buttons = CheckButtons(frame, entries=border_decorations,
                                       selected=selected,
                                       tipTexts=tipTexts)
    self.border_buttons.grid(row=0, column=1, sticky='w')

    label = Label(frame, text='    Using Font:')
    label.grid(row=0, column=2, sticky='e')
    self.border_font_list = FontList(frame, mode='Print',
                          selected=self.border_font,
                          tipText='The font used for the border texts')
    self.border_font_list.grid(row=0, column=3, sticky='w')

    row += 1
    label = Label(self, text='Line width:')
    label.grid(row=row, column=0, sticky='w')
    self.linewidth_entry = FloatEntry(self, width=10,
                                  text=self.linewidth,
                                  tipText='Line width for drawing')
    self.linewidth_entry.grid(row=row, column=1, sticky='w')
 
  def destroy(self):

    self.setOptionValues()

    if self.file_select_popup:
      self.file_select_popup.destroy()

    Frame.destroy(self)

  def getOptionValues(self):

    getOption = self.getOption
    if getOption:

      file_name = getOption('FileName', defaultValue='')
      title = getOption('Title', defaultValue='')
      x_axis_label = getOption('XAxisLabel', defaultValue='')
      y_axis_label = getOption('YAxisLabel', defaultValue='')
      paper_type = getOption('PaperSize', defaultValue=paper_types[0])
      paper_type = paper_type_dict.get(paper_type, paper_types[0])
      other_height = getOption('OtherHeight', defaultValue=10)
      other_width = getOption('OtherWidth', defaultValue=10)
      other_size = [other_height, other_width]
      other_unit = getOption('OtherUnit', defaultValue=paper_units[0])
      paper_orientation = getOption('Orientation', defaultValue=paper_orientations[0])
      in_color = getOption('InColor', defaultValue=True)
      if in_color:
        output_style = style_choices[0]
      else:
        output_style = style_choices[1]
      format_option = getOption('OutputFormat', defaultValue=format_options[0])
      output_format = format_choices[format_options.index(format_option)]
      if self.haveTicks:
        tick_outside = getOption('TickOutside', defaultValue=tick_locations[0])
        if tick_outside:
          tick_location = tick_locations.index(PrintTicks.Outside)
        else:
          tick_location = tick_locations.index(PrintTicks.Inside)
        tick_placement = getTickPlacement1(getOption('TickPlacement', defaultValue='nsew'))
      dateTime = getOption('ShowsDateTime', defaultValue=True)
      fileName = getOption('ShowsFileName', defaultValue=True)
      border_font = getOption('BorderFont', defaultValue='Helvetica 10')
      border_decoration = []
      if dateTime:
        border_decoration.append(border_decorations[0])
      if fileName:
        border_decoration.append(border_decorations[1])

      if self.haveTicks:
        spacing_choice = getOption('SpacingChoice', defaultValue=spacing_choices[0])
        x_major = getOption('XMajor', defaultValue=1.0)
        x_minor = getOption('XMinor', defaultValue=1.0)
        x_decimal = getOption('XDecimal', defaultValue=3)
        y_major = getOption('YMajor', defaultValue=1.0)
        y_minor = getOption('YMinor', defaultValue=1.0)
        y_decimal = getOption('YDecimal', defaultValue=3)

        tick_length_choice = getOption('TickLengthChoice', defaultValue=tick_length_choices[0])
        tick_major = getOption('TickMajor', defaultValue=10)
        tick_minor = getOption('TickMinor', defaultValue=5)

      scaling_choice = getOption('ScalingChoice', defaultValue=scaling_choices[0])
      scaling = getOption('Scaling', defaultValue=0.7)
      scaling = int(round(100.0 * scaling))
      x_scaling = getOption('XScaling', defaultValue=1.0)
      y_scaling = getOption('YScaling', defaultValue=1.0)

      if self.haveTicks:
        tick_font = getOption('TickFont', defaultValue='Helvetica 10')

      linewidth = getOption('LineWidth', defaultValue=Output.default_linewidth)

    else:
      file_name = ''
      title = ''
      x_axis_label = ''
      y_axis_label = ''
      paper_type = paper_types[0]
      other_unit = paper_units[0]
      other_size = ''
      paper_orientation = paper_orientations[0]
      output_style = style_choices[0]
      output_format = format_choices[0]
      if self.haveTicks:
        tick_location = tick_locations[0]
        tick_placement = tick_placements
      border_decoration = border_decorations
      border_font = 'Helvetica 10'
      if self.haveTicks:
        spacing_choice = spacing_choices[0]
        x_major = 1.0
        x_minor = 1.0
        x_decimal = 3
        y_major = 1.0
        y_minor = 1.0
        y_decimal = 3
        tick_length_choice = tick_length_choices[0]
        tick_major = 10
        tick_minor = 5
      scaling_choice = scaling_choices[0]
      scaling = 70
      x_scaling = 1.0
      y_scaling = 1.0
      if self.haveTicks:
        tick_font = 'Helvetica 10'
      linewidth = Output.default_linewidth

    if not self.haveTicks:
      tick_location = None
      tick_placement = None
      spacing_choice = spacing_choices[0]
      x_major = 1.0
      x_minor = 1.0
      x_decimal = 3
      y_major = 1.0
      y_minor = 1.0
      y_decimal = 3
      tick_font = 'Helvetica 10'
      tick_length_choice = tick_length_choices[0]
      tick_major = 10
      tick_minor = 5

    self.file_name = file_name
    self.title = title
    self.x_axis_label = x_axis_label
    self.y_axis_label = y_axis_label
    self.paper_type = paper_type
    self.other_unit = other_unit
    self.other_size = other_size
    self.paper_orientation = paper_orientation
    self.output_style = output_style
    self.output_format = output_format
    self.tick_location = tick_location
    self.tick_placement = tick_placement
    self.border_decoration = border_decoration
    self.border_font = border_font

    self.spacing_choice = spacing_choice
    self.x_major = x_major
    self.x_minor = x_minor
    self.x_decimal = x_decimal
    self.y_major = y_major
    self.y_minor = y_minor
    self.y_decimal = y_decimal

    self.scaling_choice = scaling_choices[0]
    self.scaling = scaling
    self.x_scaling = x_scaling
    self.y_scaling = y_scaling

    self.tick_font = tick_font
    self.linewidth = linewidth

    self.tick_length_choice = tick_length_choice
    self.tick_major = tick_major
    self.tick_minor = tick_minor

  def setOptionValues(self):

    if not hasattr(self, 'file_entry'):
      # it looks like on destroy can have function called but file_entry deleted already
      return

    self.file_name = file_name = self.file_entry.get()
    self.title = title = self.title_entry.get()
    self.x_axis_label = x_axis_label = self.x_axis_entry.get()
    self.y_axis_label = y_axis_label = self.y_axis_entry.get()

    n = self.size_menu.getSelectedIndex()
    self.paper_type = paper_type = paper_types[n]
    if paper_type == Output.other_paper_type:
      other_size = self.other_entry.get()
      other_unit = self.other_unit_menu.getText()
    else:
      other_size = None
      other_unit = None
    self.other_size = other_size
    self.other_unit = other_unit

    self.paper_orientation = paper_orientation = self.orientation_menu.getText()
    self.output_style = output_style = self.style_menu.getText()
    self.output_format = output_format = self.format_menu.getText()

    if self.haveTicks:
      tick_location = self.tick_menu.getText()
      tick_placement = self.tick_buttons.getSelected()
    else:
      tick_location = tick_placement = None
    self.tick_location = tick_location
    self.tick_placement = tick_placement

    self.border_decoration = border_decoration = self.border_buttons.getSelected()
    self.border_font = border_font = self.border_font_list.getText()

    if self.haveTicks:
      self.spacing_choice = spacing_choice = self.spacing_menu.getText()
      if spacing_choice != spacing_choices[0]:
        self.x_major = self.x_major_entry.get()
        self.x_minor = self.x_minor_entry.get()
        self.x_decimal = self.x_decimal_entry.get()
        self.y_major = self.y_major_entry.get()
        self.y_minor = self.y_minor_entry.get()
        self.y_decimal = self.y_decimal_entry.get()

      self.tick_length_choice = tick_length_choice = self.tick_length_menu.getText()
      if tick_length_choice != tick_length_choices[0]:
        self.tick_major = self.length_major_entry.get()
        self.tick_minor = self.length_minor_entry.get()

    self.scaling_choice = scaling_choice = self.scaling_menu.getText()
    if self.scaling_choice == scaling_choices[0]:
      scaling = self.scaling_scale.get()
      self.scaling = int(round(scaling))
    else:
      self.x_scaling = self.x_scaling_entry.get()
      self.y_scaling = self.y_scaling_entry.get()

    if self.haveTicks:
      self.tick_font = self.tick_font_list.getText()

    self.linewidth = self.linewidth_entry.get()

    setOption = self.setOption
    if setOption:
      setOption('FileName', value=file_name)
      setOption('Title', value=title)
      setOption('XAxisLabel', value=x_axis_label)
      setOption('YAxisLabel', value=y_axis_label)
      if paper_type == Output.other_paper_type:
        setOption('OtherHeight', value=other_size[0])
        setOption('OtherWidth', value=other_size[1])
        setOption('OtherUnit', value=other_unit)
      else:
        paper_type = paper_type_inverse_dict[paper_type]
        setOption('PaperSize', value=paper_type)
      setOption('Orientation', value=paper_orientation)
      in_color = (output_style == style_choices[0])
      setOption('InColor', value=in_color)
      output_format = format_options[format_choices.index(output_format)]
      setOption('OutputFormat', value=output_format)
      if self.haveTicks:
        tick_outside = (tick_location == PrintTicks.Outside)
        setOption('TickOutside', value=tick_outside)
        tick_placement = getTickPlacement2(tick_placement)
        setOption('TickPlacement', value=tick_placement)
      dateTime = (border_decorations[0] in border_decoration)
      fileName = (border_decorations[1] in border_decoration)
      setOption('ShowsDateTime', value=dateTime)
      setOption('ShowsFileName', value=fileName)
      setOption('BorderFont', value=border_font)

      if self.haveTicks:
        setOption('SpacingChoice', value=spacing_choice)
        if spacing_choice != spacing_choices[0]:
          setOption('XMajor', self.x_major)
          setOption('XMinor', self.x_minor)
          setOption('XDecimal', self.x_decimal)
          setOption('YMajor', self.y_major)
          setOption('YMinor', self.y_minor)
          setOption('YDecimal', self.y_decimal)

        setOption('TickLengthChoice', value=tick_length_choice)
        if tick_length_choice != tick_length_choices[0]:
          setOption('TickMajor', self.tick_major)
          setOption('TickMinor', self.tick_minor)

      setOption('ScalingChoice', value=scaling_choice)
      if scaling_choice == scaling_choices[0]:
        setOption('Scaling', value=0.01*self.scaling)
      else:
        setOption('XScaling', value=self.x_scaling)
        setOption('YScaling', value=self.y_scaling)

      if self.haveTicks:
        setOption('TickFont', self.tick_font)

      setOption('LineWidth', self.linewidth)

  def findFile(self):

    if self.file_select_popup:
      self.file_select_popup.open()
    else:
      file_types = [ FileType('All', ['*']),
                     FileType('PostScript', ['*.ps', '*.eps']),
                     FileType('PDF', ['*.pdf', '*.ai']) ]
      self.file_select_popup = FileSelectPopup(self, file_types=file_types)

    file = self.file_select_popup.getFile()
    if file:
      self.file_entry.set(file)

  def changedSize(self, entry):

    if entry == Output.other_paper_type:
      self.other_frame.grid(row=0, column=2, columnspan=2, sticky='w')
    else:
      self.other_frame.grid_forget()

  def changedFormat(self, entry):

    file_suffix = file_suffixes.get(entry)
    if not file_suffix:
      return

    file_name = self.file_entry.get()
    if not file_name:
      return

    for suffix in format_suffixes:
      if file_name.endswith(suffix):
        if suffix != file_suffix:
          n = len(suffix)
          file_name = file_name[:-n] + file_suffix
          self.file_entry.set(file_name)
        break
    else:
      file_name = file_name + file_suffix
      self.file_entry.set(file_name)

  def changedScaling(self, choice):

    if choice == scaling_choices[0]:
      self.scaling_scale.grid(row=0, column=2, columnspan=3, sticky='ew')
      self.x_scaling_label.grid_forget()
      self.x_scaling_entry.grid_forget()
      self.y_scaling_label.grid_forget()
      self.y_scaling_entry.grid_forget()
    else:
      self.scaling_scale.grid_forget()
      self.x_scaling_label.grid(row=0, column=2, sticky='w')
      self.x_scaling_entry.grid(row=0, column=3, columnspan=2, sticky='ew')
      self.y_scaling_label.grid(row=1, column=2, sticky='w')
      self.y_scaling_entry.grid(row=1, column=3, columnspan=2, sticky='ew')

    self.setOptionValues()

  def changedSpacing(self, choice):

    if choice == spacing_choices[0]:
      self.spacing_frame.grid_forget()
    else:
      self.spacing_frame.grid(row=1, column=1, columnspan=3, sticky='ew')

    self.setOptionValues()

  def changedLength(self, choice):

    if choice == tick_length_choices[0]:
      self.length_frame.grid_forget()
    else:
      self.length_frame.grid(row=1, column=0, columnspan=4, sticky='ew')

    self.setOptionValues()

  # width and height are of plot, in pixels
  def getOutputHandler(self, pixel_width, pixel_height, unit_width=1.0, unit_height=1.0, fonts=None):

    if not fonts:
      fonts = []
    else:
      fonts = list(fonts)

    for n in range(len(fonts)):
      if fonts[n] == 'Times':
        fonts[n] = 'Times-Roman'

    self.setOptionValues()

    if not self.file_name:
      showError('No file', 'No file specified', parent=self)
      return None

    x_scaling = y_scaling = 1.0
    if self.scaling_choice != scaling_choices[0]:
      try:
        x_scaling = float(self.x_scaling)
      except:
        showError('Bad X Scaling', 'Specified X Scaling must be floating point', parent=self)
        return None
      try:
        y_scaling = float(self.y_scaling)
      except:
        showError('Bad Y Scaling', 'Specified Y Scaling must be floating point', parent=self)
        return None

    if os.path.exists(self.file_name):
      if not showYesNo('File exists', 'File "%s" exists, overwrite?' % self.file_name, parent=self):
        return None

    if self.paper_type == Output.other_paper_type:
      paper_size = self.other_size + [ self.other_unit ]
    else:
      paper_size = paper_sizes[self.paper_type]

    output_scaling = self.scaling / 100.0

    border_font = self.border_font
    (font, size) = border_font.split()
    size = int(size)
    border_text = {}
    for decoration in self.border_decoration:
      if decoration == 'Time and Date':
        location = 'se'
        text = time.ctime(time.time())
      elif decoration == 'File Name':
        location = 'sw'
        text = self.file_name
      else:
        continue # should not be here
      border_text[location] = (text, font, size)
    if self.title:
      location = 'n'
      border_text[location] = (self.title, font, size+6)

    if font not in fonts:
      fonts.append(font)

    if self.haveTicks and self.tick_location == PrintTicks.Outside:
      axis_label_offset = 2
    else:
      axis_label_offset = 0
     
    outputHandler = PrintHandler.getOutputHandler(self.file_name,
                              pixel_width, pixel_height,
                              unit_width, unit_height,
                              scaling_choice=self.scaling_choice,
                              output_scaling=output_scaling,
                              w_scaling=x_scaling,
                              h_scaling=y_scaling,
                              paper_size=paper_size,
                              paper_orientation=self.paper_orientation,
                              output_style=self.output_style,
                              output_format=self.output_format,
                              border_text=border_text,
                              x_axis_label=self.x_axis_label,
                              y_axis_label=self.y_axis_label,
                              axis_label_font=font,
                              axis_label_size=size,
                              axis_label_offset=axis_label_offset,
                              fonts=fonts,
                              linewidth=self.linewidth,
                              do_outline_box=self.doOutlineBox)

    return outputHandler

  def getAspectRatio(self):

    self.setOptionValues()

    if self.paper_type == Output.other_paper_type:
      paper_size = self.other_size
    else:
      paper_size = paper_sizes[self.paper_type]

    r = paper_size[1] / paper_size[0]
    if self.paper_orientation == 'Landscape':
      r = 1.0 / r

    return r
Ejemplo n.º 10
0
class CreateAxisTypePopup(BasePopup):
    def __init__(self, parent, *args, **kw):

        self.measurementType = None

        BasePopup.__init__(self,
                           parent=parent,
                           title='Create axis type',
                           modal=True,
                           **kw)

    def body(self, master):

        master.grid_columnconfigure(1, weight=1)

        row = 0
        label = Label(master, text='Axis name: ', grid=(row, 0))
        tipText = 'Short text name for new type of axis e.g. "17O"'
        self.name_entry = Entry(master,
                                width=15,
                                grid=(row, 1),
                                tipText=tipText)

        row += 1
        label = Label(master, text='Axis region: ', grid=(row, 0))
        tipText = 'Comma separated values for the upper and lower bound of the axis allowed range of values'
        self.region_entry = FloatEntry(master,
                                       text=[0.0, 1.0],
                                       isArray=True,
                                       width=15,
                                       grid=(row, 1),
                                       tipText=tipText)

        row += 1
        label = Label(master, text='Measurement type:', grid=(row, 0))
        tipText = 'The physical entity that is being measured along the axis'
        self.measurement_list = PulldownList(master, tipText=tipText)
        self.measurement_list.grid(row=row, column=1, sticky='w')

        row += 1
        label = Label(master, text='Dimension is sampled: ', grid=(row, 0))
        tipText = 'Whether the axis is discretely sampled or a continuous range (albeit on a grid)'
        self.sampled_popup = BooleanPulldownMenu(master,
                                                 grid=(row, 1),
                                                 tipText=tipText)

        row += 1
        label = Label(master, text='Decimal places: ', grid=(row, 0))
        tipText = 'The number of decimal places that the axis values are rounded to for display purposes'
        self.decimals_entry = IntEntry(master,
                                       text=0,
                                       width=15,
                                       grid=(row, 1),
                                       tipText=tipText)

        row += 1
        label = Label(master, text='Peak size: ', grid=(row, 0))
        tipText = 'The relative scale for the peak symbol (i.e the "X" shape) size compared to other axes'
        self.peak_size_entry = FloatEntry(master,
                                          text=1.0,
                                          width=15,
                                          grid=(row, 1),
                                          tipText=tipText)

        row += 1
        label = Label(master, text='Allowed axis units:', grid=(row, 0))
        tipTexts = [
            'Units of measurement allowed for this kind of axis',
        ]
        units = [au.unit for au in self.parent.getAxisUnits()]
        selected = [True] * len(units)
        self.units_list = CheckButtons(master,
                                       units,
                                       selected=selected,
                                       direction='vertical',
                                       grid=(row, 1),
                                       tipTexts=tipTexts)

        row += 1
        tipTexts = [
            'Make a new axis specification of the selected type and close this popup'
        ]
        texts = ['Create']
        commands = [self.ok]
        buttons = UtilityButtonList(master,
                                    texts=texts,
                                    commands=commands,
                                    doClone=False,
                                    closeText='Cancel',
                                    helpUrl=self.help_url,
                                    grid=(row, 0),
                                    gridSpan=(1, 2),
                                    tipTexts=tipTexts)

        master.grid_rowconfigure(row, weight=1)

        self.update()

    def update(self, *extra):

        measurementType = self.measurementType
        measurementTypes = self.parent.getMeasurementTypes()
        if measurementTypes:
            if measurementType not in measurementTypes:
                self.measurementType = measurementType = measurementTypes[0]
            index = measurementTypes.index(measurementType)
        else:
            index = 0
            self.measurementType = None

        self.measurement_list.setup(measurementTypes, None, index)

    def apply(self):

        name = self.name_entry.get()
        if (not name):
            showError('No name', 'Need to enter name', self)
            return False

        names = [axisType.name for axisType in self.analysisProject.axisTypes]
        if (name in names):
            showError('Repeated name', 'Name already used', self)
            return False

        region = self.region_entry.get()
        if ((region is None) or (len(region) != 2)):
            showError('Region error',
                      'Region must be float array of length two', self)
            return False

        if (region[0] >= region[1]):
            showError('Region error',
                      'Region must have first number < second number', self)
            return False

        measurementType = self.measurement_list.getText()
        isSampled = self.sampled_popup.getSelected()

        numDecimals = self.decimals_entry.get()
        if ((numDecimals is None) or (numDecimals < 0)):
            showError('Decimals error',
                      'Number of decimal places must be >= 0', self)
            return False

        peakSize = self.peak_size_entry.get()
        if ((peakSize is None) or (peakSize <= 0)):
            showError('Peak size error', 'Peak size must be > 0', self)
            return False

        selected = self.units_list.getSelected()
        allUnits = self.parent.getAxisUnits()
        axisUnits = [au for au in allUnits if au.unit in selected]

        self.analysisProject.newAxisType(name=name,
                                         region=region,
                                         isSampled=isSampled,
                                         axisUnits=axisUnits,
                                         numDecimals=numDecimals,
                                         peakSize=peakSize,
                                         measurementType=measurementType)

        return True
Ejemplo n.º 11
0
class SelectionListPopup(TemporaryBasePopup):
 
  def __init__(self, parent, selectionList, title = 'Select', text = 'Select', topText = None, dismissText = None, selected = None, selectionDict = None, urlFile = None, dismissButton = True, modal = False):
  
    self.selectionList = selectionList
    self.selectionDict = selectionDict
    
    self.text = text
    
    self.dismissButton = dismissButton
    
    if dismissButton:
      if dismissText:
        self.dismissText = dismissText
      else:
        self.dismissText = 'dismiss'
      
    self.topText = topText
    self.isSelected = None

    if not selected:
      self.selectedIndex = 0
    else:
      self.selectedIndex = self.selectionList.index(selected)
      
    if urlFile:
      self.help_url = joinPath(getHelpUrlDir(),urlFile + '.html')
    else:
      self.help_url = None
          
    TemporaryBasePopup.__init__(self,parent = parent, title = title, modal = modal, transient=True)
 
  def body(self, master):
    
    #
    # Popup window
    #

    row = 0
    
    if self.topText:
      label = Label(master, text= self.topText)
      label.grid(row=row, column=0, columnspan = 2, sticky=Tkinter.EW)
    
      row = row + 1

    label = Label(master, text= self.text)
    label.grid(row=row, column=0, sticky=Tkinter.EW)

    self.menu = PulldownList(master, texts = self.selectionList, index = self.selectedIndex)
    self.menu.grid(row=row, column=1, sticky=Tkinter.E, ipadx = 20)
 
    row = row + 1
    texts = [ 'OK' ]
    commands = [ self.ok ]   # This calls 'ok' in BasePopup, this then calls 'apply' in here
    
    if self.dismissButton:
      buttons = createDismissHelpButtonList(master, texts=texts, commands=commands, dismiss_text = self.dismissText, help_url=self.help_url)
    else:
      buttons = createHelpButtonList(master, texts=texts, commands=commands, help_url=self.help_url)

    buttons.grid(row=row, column=0, columnspan = 3)
   

  def apply(self):
        
    self.isSelected = self.menu.getText()
    
    if self.selectionDict:
      self.selection = self.selectionDict[self.isSelected]
    else:
      self.selection = self.isSelected

    return True