示例#1
0
class PrintWindowPopup(BasePopup):

  """
  **Print Window to Output File**

  The purpose of this dialog is to allow the saving of the drawing of
  one of the spectrum windows to a file, in one of the following formats:
  PostScript (PS), Encapsulated PostScript (EPS) or Portable Document
  Format (PDF).

  The one window that is being printed out is specified at the top.
  There are four tabs.  The first one, Options, is the most important.
  In particular, it is used to specify the File name.  At its simplest
  to print out a window you just need to specify the File name, and
  then click "Save Print File".  But it is likely you will at the very
  least want to change some of the settings in the Options tab.

  You can specify a Title and a label for the X axis and/or Y axis.

  This tab is also used to specify the Paper size (default A4), the
  Orientation of the paper (default Portrait), whether the printout
  is Color or Black and white (the Style, default Color), and what
  the Format is (PS, EPS or PDF, default PS).

  The ticks for the rulers can be chosen to be Inside or Outside the
  main frame and can be in any combination of the Top, Bottom, Left
  or Right side of the main frame.  The Tick Font includes the option
  of not printing the tick labels at all.  The Tick spacing between
  the major and minor ticks can be set automatically (so the program
  determines it) or manually.  For the latter the user has to specify
  the Major and Minor spacings in terms of the unit of the display
  (normally ppm), and also the number of decimal places for the Tick
  labels.

  How the main frame fits into the paper is determined by the Scaling
  option.  The Percentage option just means that the main frame is
  scaled by that amount relative to the biggest size it could be and
  still fit on the paper.  The remaining options are if you want to
  specify the cms or inches per unit (normally ppm), or the inverse
  of these.  In this case it could be the case that the main frame
  actually exceeds the size of the paper.

  You can also include the Time and Date and/or the File Name in the
  printout, and you can specify the font used for this.  The same
  font is used for the Title and X and Y axis labels, except that
  the Title font is 6 pts bigger.

  Finally, you can also set the linewidth, in points (the default is 0.1).

  The other three tabs provide fine tuning of what is output.  In many
  cases they can be ignored.

  The Spectra tab lets you choose settings for which of the window's
  spectra are drawn, in terms of both the positive and negative contours.
  This is independent of what is actually drawn on the screen.  But you
  need to check the "Use below settings when printing" checkbutton if you
  want the values specified here to be used rather than the screen settings.
  The values in the table are initially set to be the screen values but
  afterwards can only be changed manually.  Clicking on the "Reset Selected"
  button changes the values in the table to the current screen ones.

  The Peak Lists tab is similar, except it applies to the peak lists in
  the window rather than the spectra.  Again, if you want the values in
  the table to be used then you need to check the "Use below settings
  when printing" checkbutton.

  The Region tab is for specifying the region for the x, y and orthogonal
  axes, if you do not want to use the regions as seen in the window on the
  screen.  Again, you have to check "Use override region when printing"
  checkbutton to actually have the values in the table be used in the
  printout.  By default, the override region is set to the current
  window region if it is not set already, otherwise it is left to the
  previous value unless you click the "Set Region from Window" or the
  "Set Width from Window" or the "Set Center from Window" buttons.
  And the override region can be specified either using the min and max
  values of the region, or the center and width.
"""

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

    self.waiting = False
    
    title='Window : Print Window'
    BasePopup.__init__(self, parent=parent, title=title, **kw)

  def body(self, guiFrame):

    self.geometry('600x350')

    project = self.project
    analysisProject = self.analysisProject

    guiFrame.grid_columnconfigure(1, weight=1)

    row = 0
    frame = Frame(guiFrame, grid=(0,0))
    label = Label(frame, text=' Window:', grid=(0,0))
    self.windowPulldown = PulldownList(frame, grid=(0,1),
                                      tipText='The window that will be printed out',
                                      callback=self.selectWindow)
                                      
    tipTexts = ['For the window pulldown, show all of the spectrum windows in the current project, irrespective of the active group',
                'For the window pulldown, show only spectrum windows that are in the currently active window group']
    self.whichWindows = RadioButtons(frame, grid=(0,2),
                                     entries=ACTIVE_OPTIONS, tipTexts=tipTexts,
                                     select_callback=self.updateWindows)
    
    texts = [ 'Save Print File' ]
    tipTexts = [ 'Save the printout to the specified file' ]
    commands = [ self.saveFile ]
    buttons = UtilityButtonList(guiFrame, helpUrl=self.help_url, grid=(row,2),
                                commands=commands, texts=texts, tipTexts=tipTexts)
    self.buttons = buttons
    buttons.buttons[0].config(bg='#B0FFB0')
    
    row += 1
    guiFrame.grid_rowconfigure(row, weight=1)
    options = ['Options', 'Spectra', 'Peak Lists', 'Region']
    tipTexts = ['Optional settings for spectra', 'Optional settings for peak lists', 'Optional settings for the region']
    tabbedFrame = TabbedFrame(guiFrame, options=options, tipTexts=tipTexts)
    tabbedFrame.grid(row=row, column=0, columnspan=3, sticky='nsew')
    self.tabbedFrame = tabbedFrame

    optionFrame, spectrumFrame, peakListFrame, regionFrame = tabbedFrame.frames

    optionFrame.expandGrid(0, 0)
    getOption = lambda key, defaultValue: PrintBasic.getPrintOption(analysisProject, key, defaultValue)
    setOption = lambda key, value: PrintBasic.setPrintOption(analysisProject, key, value)
    self.printFrame = PrintFrame(optionFrame, getOption=getOption,
                                 grid=(0,0), gridSpan=(1,1),
                                 setOption=setOption, haveTicks=True,
                                 doOutlineBox=False)

    spectrumFrame.expandGrid(0, 0)
    frame = Frame(spectrumFrame, grid=(0,0), gridSpan=(1,1))
    frame.expandGrid(1,0)

    self.overrideSpectrum = CheckButton(frame,
       text='Use below settings when printing',
       tipText='Use below settings when printing instead of the window values',
       grid=(0,0), sticky='w')

    tipText = 'Change the settings of the selected spectra back to their window values'
    button = Button(frame, text='Reset Selected', tipText=tipText,
                    command=self.resetSelected, grid=(0,1), sticky='e')

    self.posColorPulldown = PulldownList(self, callback=self.setPosColor)
    self.negColorPulldown = PulldownList(self, callback=self.setNegColor)
    headings = ['Spectrum', 'Pos. Contours\nDrawn', 'Neg. Contours\nDrawn', 'Positive\nColours', 'Negative\nColours']
    tipTexts = ['Spectrum in window', 'Whether the positive contours should be drawn', 'Whether the negative contours should be drawn',
      'Colour scheme for positive contours (can be a single colour)', 'Colour scheme for negative contours (can be a single colour)']

    editWidgets      = [ None, None, None, self.posColorPulldown, self.negColorPulldown]
    editGetCallbacks = [ None, self.togglePos, self.toggleNeg, self.getPosColor, self.getNegColor]
    editSetCallbacks = [ None, None, None, self.setPosColor, self.setNegColor]
    self.spectrumTable = ScrolledMatrix(frame, headingList=headings,
                                        tipTexts=tipTexts,
                                        multiSelect=True,
                                        editWidgets=editWidgets,
                                        editGetCallbacks=editGetCallbacks,
                                        editSetCallbacks=editSetCallbacks,
                                        grid=(1,0), gridSpan=(1,2))

    peakListFrame.expandGrid(0, 0)
    frame = Frame(peakListFrame, grid=(0,0), gridSpan=(1,3))
    frame.expandGrid(1,0)

    self.overridePeakList = CheckButton(frame,
       text='Use below settings when printing',
       tipText='Use below settings when printing instead of the window values',
       grid=(0,0))

    tipText = 'Change the settings of the selected peak lists back to their window values'
    button = Button(frame, text='Reset Selected', tipText=tipText,
                    command=self.resetSelected, grid=(0,1), sticky='e')

    colors = Color.standardColors
    self.peakColorPulldown = PulldownList(self, callback=self.setPeakColor,
                                        texts=[c.name for c in colors],
                                        objects=[c.hex for c in colors],
                                        colors=[c.hex for c in colors])
    headings = [ 'Peak List', 'Symbols Drawn', 'Peak Font', 'Peak Colour']
    self.fontMenu = FontList(self, mode='Print', extraTexts=[no_peak_text])
    editWidgets      = [ None, None, self.fontMenu, self.peakColorPulldown]
    editGetCallbacks = [ None, self.togglePeaks, self.getPeakFont, self.getPeakColor ]
    editSetCallbacks = [ None, None, self.setPeakFont, self.setPeakColor ]
    self.peakListTable = ScrolledMatrix(frame, headingList=headings,
                                        multiSelect=True,
                                        editWidgets=editWidgets,
                                        editGetCallbacks=editGetCallbacks,
                                        editSetCallbacks=editSetCallbacks,
                                        grid=(1,0), gridSpan=(1,2))

    regionFrame.expandGrid(0, 0)
    frame = Frame(regionFrame, grid=(0,0), gridSpan=(1,3))
    frame.expandGrid(3,0)
    tipText = 'Use the specified override region when printing rather than the window values'
    self.overrideButton = CheckButton(frame, text='Use override region when printing',
                                      tipText=tipText,
                                      callback=self.toggledOverride, grid=(0,0))

    tipTexts = ('Use min and max to specify override region', 'Use center and width to specify override region')
    self.use_entry = USE_ENTRIES[0]
    self.useButtons = RadioButtons(frame, entries=USE_ENTRIES,
                                      tipTexts=tipTexts,
                                      select_callback=self.changedUseEntry,
                                      grid=(1,0))

    texts = ('Set Region from Window', 'Set Center from Window', 'Set Width from Window')
    tipTexts = ('Set the override region to be the current window region',
                'Set the center of the override region to be the center of the current window region',
                'Set the width of the override region to be the width of the current window region')
    commands = (self.setRegionFromWindow, self.setCenterFromWindow, self.setWidthFromWindow)
    self.setRegionButton = ButtonList(frame, texts=texts,
                                      tipTexts=tipTexts,
                                      commands=commands, grid=(2,0))

    self.minRegionWidget = FloatEntry(self, returnCallback=self.setMinRegion, width=10)
    self.maxRegionWidget = FloatEntry(self, returnCallback=self.setMaxRegion, width=10)
    headings = MIN_MAX_HEADINGS
    editWidgets      = [ None, None, self.minRegionWidget, self.maxRegionWidget ]
    editGetCallbacks = [ None, None, self.getMinRegion,    self.getMaxRegion ]
    editSetCallbacks = [ None, None, self.setMinRegion,    self.setMaxRegion ]
    self.regionTable = RegionScrolledMatrix(frame, headingList=headings,
                                            editWidgets=editWidgets,
                                            editGetCallbacks=editGetCallbacks,
                                            editSetCallbacks=editSetCallbacks,
                                            grid=(3,0))

    self.updateWindows()
    self.updateAfter()
    
    self.administerNotifiers(self.registerNotify)
    
  def administerNotifiers(self, notifyFunc):
  
    for func in ('__init__', 'delete', 'setOverrideRegion'):
      notifyFunc(self.updateAfter, 'ccpnmr.Analysis.AxisRegion', func)

    notifyFunc(self.updateAfter, 'ccpnmr.Analysis.SpectrumWindow', 'setUseOverrideRegion')

    for func in ('__init__', 'delete', 'setName'):
      notifyFunc(self.updateWindows, 'ccpnmr.Analysis.SpectrumWindow', func)
      notifyFunc(self.updateWindows, 'ccpnmr.Analysis.SpectrumWindowPane', func)
      
    for func in ('addSpectrumWindow', 'removeSpectrumWindow'):
      notifyFunc(self.updateWindows, 'ccpnmr.Analysis.SpectrumWindowGroup', func)

  def destroy(self):

    self.administerNotifiers(self.unregisterNotify)

    BasePopup.destroy(self)

  def changedUseEntry(self, entry):

    self.use_entry = entry
    self.updateRegionTable()

  def resetSpectrum(self):

    spectrumWindowViews = self.spectrumTable.currentObjects
    analysisSpectra = set()
    for spectrumWindowView in spectrumWindowViews:
      PrintBasic.setPrintOption(spectrumWindowView, 'PositiveOn', spectrumWindowView.isPosVisible)
      PrintBasic.setPrintOption(spectrumWindowView, 'NegativeOn', spectrumWindowView.isNegVisible)
      analysisSpectra.add(spectrumWindowView.analysisSpectrum)
    for analysisSpectrum in analysisSpectra:
      PrintBasic.setPrintOption(analysisSpectrum, 'PositiveColors', analysisSpectrum.posColors)
      PrintBasic.setPrintOption(analysisSpectrum, 'NegativeColors', analysisSpectrum.negColors)

    self.updateAfter()

  def resetPeakList(self):

    windowPeakLists = self.peakListTable.currentObjects
    analysisPeakLists = set()
    for windowPeakList in windowPeakLists:
      PrintBasic.setPrintOption(windowPeakList, 'PeaksOn', windowPeakList.isSymbolDrawn)
      PrintBasic.setPrintOption(windowPeakList, 'PeakFont', windowPeakList.spectrumWindowView.analysisSpectrum.font)
      analysisPeakLists.add(windowPeakList.analysisPeakList)
    for analysisPeakList in analysisPeakLists:
      PrintBasic.setPrintOption(analysisPeakList, 'PeakColor', analysisPeakList.symbolColor)

    self.updateAfter()

  def resetSelected(self):

    n = self.tabbedFrame.selected
    if n == 0:
      self.resetSpectrum()
    elif n == 1:
      self.resetPeakList()

  def togglePos(self, spectrumWindowView):

    PrintBasic.setPrintOption(spectrumWindowView, 'PositiveOn',
      not PrintBasic.getPrintOption(spectrumWindowView, 'PositiveOn', defaultValue=spectrumWindowView.isPosVisible))

    self.updateAfter()

  def toggleNeg(self, spectrumWindowView):

    PrintBasic.setPrintOption(spectrumWindowView, 'NegativeOn',
      not PrintBasic.getPrintOption(spectrumWindowView, 'NegativeOn', defaultValue=spectrumWindowView.isNegVisible))

    self.updateAfter()

  def getPosColor(self, spectrumWindowView):

    schemes = getHueSortedColorSchemes(self.analysisProfile)
    names = [s.name for s in schemes]
    colors = [list(s.colors) for s in schemes]
    index = 0
    
    analysisSpec = spectrumWindowView.analysisSpectrum
    posColors = list(PrintBasic.getPrintOption(analysisSpec, 'PositiveColors', defaultValue=analysisSpec.posColors))
    
    if posColors in colors:
      index = colors.index(posColors)
      
    self.posColorPulldown.setup(names, schemes, index, colors)

  def setPosColor(self, *extra):

    spectrumWindowView = self.spectrumTable.currentObject
    if spectrumWindowView:
      analysisSpec = spectrumWindowView.analysisSpectrum
      PrintBasic.setPrintOption(analysisSpec, 'PositiveColors', self.posColorPulldown.getObject().colors)
      self.updateSpectrumTable()
    
  def getNegColor(self, spectrumWindowView):

    schemes = getHueSortedColorSchemes(self.analysisProfile)
    names = [s.name for s in schemes]
    colors = [list(s.colors) for s in schemes]
    index = 0
    
    analysisSpec = spectrumWindowView.analysisSpectrum
    negColors = list(PrintBasic.getPrintOption(analysisSpec, 'NegativeColors', defaultValue=analysisSpec.negColors))
    
    if negColors in colors:
      index = colors.index(negColors)
      
    self.negColorPulldown.setup(names, schemes, index, colors)

  def setNegColor(self, *extra):

    spectrumWindowView = self.spectrumTable.currentObject
    if spectrumWindowView:
      analysisSpec = spectrumWindowView.analysisSpectrum
      PrintBasic.setPrintOption(analysisSpec, 'NegativeColors', self.negColorPulldown.getObject().colors)
      self.updateSpectrumTable()
    
  def getPeakColor(self, windowPeakList):
 
    color = windowPeakList.analysisPeakList.symbolColor
    self.peakColorPulldown.set(color)
 
  def setPeakColor(self, *extra):

    windowPeakList = self.peakListTable.currentObject
    if windowPeakList:
      color = self.peakColorPulldown.getObject()
      scheme = self.analysisProfile.findFirstColorScheme(colors=(color,))
      if scheme:
        color = scheme.name
      analysisPeakList = windowPeakList.analysisPeakList
      PrintBasic.setPrintOption(analysisPeakList, 'PeakColor', color)
      self.updatePeakListTable()
  
  def togglePeaks(self, windowPeakList):

    PrintBasic.setPrintOption(windowPeakList, 'PeaksOn',
      not PrintBasic.getPrintOption(windowPeakList, 'PeaksOn', defaultValue=windowPeakList.isSymbolDrawn))

    self.updateAfter()

  def getPeakFont(self, windowPeakList):

    if windowPeakList.isAnnotationDrawn:
      default = windowPeakList.analysisPeakList.analysisSpectrum.font
    else:
      default = no_peak_text
    font = PrintBasic.getPrintOption(windowPeakList, 'PeakFont', defaultValue=default)

    self.fontMenu.set(font)

  def setPeakFont(self, windowPeakList):

    PrintBasic.setPrintOption(windowPeakList, 'PeakFont', self.fontMenu.getText())

    self.updateAfter()

  def updateWindows(self, obj=None):

    useAll = (self.whichWindows.get() == ACTIVE_OPTIONS[0])
    
    index = 0
    windowPane = self.windowPulldown.getObject()
    windowPanes = []
    names = []
    
    if not windowPane:
      application = self.project.application
      name = application.getValue(self.analysisProject, keyword='printWindowWindow')
      if useAll:
        window = self.analysisProject.findFirstSpectrumWindow(name=name)
        if window:
          windowPane = window.findFirstSpectrumWindowPane()
      else:
        for window in self.analysisProject.sortedSpectrumWindows():
          if isActiveWindow(window):
            windowPane = window.findFirstSpectrumWindowPane()
            break
        else:
          window = None

    for window in self.analysisProject.sortedSpectrumWindows():
      if useAll or isActiveWindow(window):
        for windowPane0 in window.sortedSpectrumWindowPanes():
          windowPanes.append(windowPane0)
          names.append(getWindowPaneName(windowPane0))
    
    if windowPanes:
      if windowPane not in windowPanes:
        windowPane = windowPanes[0]
      
      index = windowPanes.index(windowPane)  
    
    else:
      windowPane = None
      
    self.selectWindow(windowPane)
        
    self.windowPulldown.setup(names, windowPanes, index)

  def saveFile(self):

    windowPane = self.windowPulldown.getObject()

    if not windowPane:
      return

    axisPanels = windowPane.sortedAxisPanels()
    aspectRatio = self.getPrintAspectRatio()
    pixelWidth = self.totalSize(axisPanels[0])
    pixelHeight = aspectRatio*self.totalSize(axisPanels[1])
    unitWidth = self.totalOverrideRegion(axisPanels[0])
    unitHeight = self.totalOverrideRegion(axisPanels[1])

    printFrame = self.printFrame

    isOverrideSpectrumSelected = self.overrideSpectrum.isSelected()
    if isOverrideSpectrumSelected:
      spectrumWindowViews = self.spectrumTable.objectList
      # alternatively, spectrumWindowViews = windowPane.spectrumWindowViews
      analysisSpectra = set()
      for spectrumWindowView in spectrumWindowViews:
        spectrumWindowView.printPositive = PrintBasic.getPrintOption(spectrumWindowView, 'PositiveOn', spectrumWindowView.isPosVisible)
        spectrumWindowView.printNegative = PrintBasic.getPrintOption(spectrumWindowView, 'NegativeOn', spectrumWindowView.isNegVisible)
        analysisSpectra.add(spectrumWindowView.analysisSpectrum)
        
      for analysisSpectrum in analysisSpectra:
        analysisSpectrum.printPositiveColors = PrintBasic.getPrintOption(analysisSpectrum, 'PositiveColors', analysisSpectrum.posColors)
        analysisSpectrum.printNegativeColors = PrintBasic.getPrintOption(analysisSpectrum, 'NegativeColors', analysisSpectrum.negColors)

    isOverridePeakListSelected = self.overridePeakList.isSelected()
    if isOverridePeakListSelected:
      windowPeakLists = self.peakListTable.objectList
      analysisPeakLists = set()
      for windowPeakList in windowPeakLists:
        windowPeakList.printPeaks = PrintBasic.getPrintOption(windowPeakList, 'PeaksOn', windowPeakList.isSymbolDrawn)
        if windowPeakList.isAnnotationDrawn:
          default = windowPeakList.analysisPeakList.analysisSpectrum.font
        else:
          default = no_peak_text
        windowPeakList.printFont = PrintBasic.getPrintOption(windowPeakList, 'PeakFont', default)
        analysisPeakLists.add(windowPeakList.analysisPeakList)
      for analysisPeakList in analysisPeakLists:
        analysisPeakList.printColor = PrintBasic.getPrintOption(analysisPeakList, 'PeakColor', analysisPeakList.symbolColor)

    xrr = axisPanels[0].findFirstAxisRegion().region
    dxx = abs(xrr[0]-xrr[1])
    yrr = axisPanels[1].findFirstAxisRegion().region
    dyy = abs(yrr[0]-yrr[1])
    try:
      outputHandler = printFrame.getOutputHandler(pixelWidth, pixelHeight, unitWidth, unitHeight, fonts=printNames)
      if not outputHandler:
        return
      
      analysisProject = self.analysisProject
      major_minor_dict = {}
      spacing_choice = PrintBasic.getPrintOption(analysisProject, 'SpacingChoice', spacing_choices[0])
      if spacing_choice != spacing_choices[0]:
        for attr in ('XMajor', 'XMinor', 'XDecimal',
                     'YMajor', 'YMinor', 'YDecimal',):
          val = PrintBasic.getPrintOption(analysisProject, attr, None)
          if val is not None:
            major_minor_dict[attr] = val

      tick_length_choice = PrintBasic.getPrintOption(analysisProject, 'TickLengthChoice', tick_length_choices[0])
      if tick_length_choice != tick_length_choices[0]:
        for attr in ('TickMajor', 'TickMinor'):
          val = PrintBasic.getPrintOption(analysisProject, attr, None)
          if val is not None:
            major_minor_dict[attr] = val

      windowDraw = WindowDraw(self.parent, windowPane)

      PrintBasic.printWindow(windowDraw,
                             outputHandler,
                             printFrame.tick_location,
                             printFrame.tick_placement,
                             aspectRatio,
                             printFrame.tick_font,
                             major_minor_dict)
      
      msg = 'Saved to file "%s"' % printFrame.file_name                        
      showInfo('Success', msg, parent=self)
      
    except IOError, e:
      showError('IO Error', str(e), parent=self)

    if isOverrideSpectrumSelected:
      analysisSpectra = set()
      for spectrumWindowView in spectrumWindowViews:
        del spectrumWindowView.printPositive
        del spectrumWindowView.printNegative
        analysisSpectra.add(spectrumWindowView.analysisSpectrum)
      for analysisSpectrum in analysisSpectra:
        del analysisSpectrum.printPositiveColors
        del analysisSpectrum.printNegativeColors

    if isOverridePeakListSelected:
      analysisPeakLists = set()
      for windowPeakList in windowPeakLists:
        del windowPeakList.printPeaks
        del windowPeakList.printFont
        analysisPeakLists.add(windowPeakList.analysisPeakList)
      for analysisPeakList in analysisPeakLists:
        del analysisPeakList.printColor
示例#2
0
class DangleFrame(Frame):
    def __init__(self, parent, dangleGui, project=None, *args, **kw):

        self.guiParent = parent
        self.dangleGui = dangleGui
        self.dangleDir = None
        self.dangleChain = None
        self.dangleResidue = None
        #self.outDir      = OUTDIR
        self.row = None
        self.col = None
        self.project = project
        self.nmrProject = None
        self.colorScheme = 'red'

        self.chain = None
        self.shiftList = None
        self.dangleStore = False  # Not None
        self.constraintSet = None
        self.ensemble = None

        Frame.__init__(self, parent=parent)

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

        row = 0

        # TOP LEFT FRAME

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

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

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

        label = Label(frame, text='Max No. of Islands:')
        label.grid(row=0, column=4, sticky='w')
        sizes = range(10)
        texts = [str(s) for s in sizes] + [
            'Do not reject',
        ]
        self.rejectPulldown = PulldownList(
            frame,
            texts=texts,
            objects=sizes + [
                None,
            ],
            tipText=
            'Select the maximum allowed number of disontinuous prediction islands'
        )
        self.rejectPulldown.set(DEFAULT_MAX_ISLANDS)  # Actual value not index
        self.rejectPulldown.grid(row=0, column=5, sticky='w')

        label = Label(frame, text='Dangle Run:')
        label.grid(row=1, column=0, sticky='w')
        self.dangleStorePulldown = PulldownList(
            frame,
            callback=self.changeDangleStore,
            tipText='Select a run number to store DANGLE results within')
        self.dangleStorePulldown.grid(row=1, column=1, sticky='w')

        label = Label(frame, text='Ensemble:')
        label.grid(row=1, column=2, sticky='w')
        self.ensemblePulldown = PulldownList(
            frame,
            callback=self.changeEnsemble,
            tipText=
            'Select the structure ensemble for superimposition of angle values on the GLE plots'
        )
        self.ensemblePulldown.grid(row=1, column=3, sticky='w')

        label = Label(frame, text='Restraint Set:')
        label.grid(row=1, column=4, sticky='w')
        self.constrSetPulldown = PulldownList(
            frame,
            callback=self.changeConstraintSet,
            tipText=
            'Select the CCPN restraint set to store DANGLE dihedral angle restraints in'
        )
        self.constrSetPulldown.grid(row=1, column=5, sticky='w')

        # TOP RIGHT FRAME

        outerFrame = Frame(self)
        outerFrame.grid(row=row, column=1, rowspan=2, sticky='nsew')
        outerFrame.rowconfigure(0, weight=1)
        outerFrame.columnconfigure(0, weight=1)

        frame = LabelFrame(outerFrame, text='Global Likelihood Estimates')
        frame.grid(row=0, column=0, sticky='nsew')
        frame.rowconfigure(1, weight=1)
        frame.columnconfigure(2, weight=1)

        self.prevPlot = ViewRamachandranFrame(frame,
                                              relief='sunken',
                                              defaultPlot=False,
                                              width=180,
                                              height=180,
                                              bgColor=self.cget('bg'),
                                              nullColor='#000000',
                                              titleText='Previous',
                                              xTicks=False,
                                              yTicks=False,
                                              xLabel='',
                                              yLabel='',
                                              showCoords=False)
        self.prevPlot.grid(row=0, column=0, sticky='nsew')
        self.prevPlot.getPlotColor = self.getPlotColor

        self.nextPlot = ViewRamachandranFrame(frame,
                                              relief='sunken',
                                              defaultPlot=False,
                                              width=180,
                                              height=180,
                                              bgColor=self.cget('bg'),
                                              nullColor='#000000',
                                              titleText='Next',
                                              xTicks=False,
                                              yTicks=False,
                                              xLabel='',
                                              yLabel='',
                                              showCoords=False)
        self.nextPlot.grid(row=0, column=1, sticky='nsew')
        self.nextPlot.getPlotColor = self.getPlotColor

        self.plot = ViewRamachandranFrame(frame,
                                          relief='sunken',
                                          defaultPlot=False,
                                          width=360,
                                          height=360,
                                          bgColor=self.cget('bg'),
                                          nullColor='#000000')
        self.plot.grid(row=1, column=0, columnspan=2, sticky='nsew')
        self.plot.selectColor = '#FFB0B0'
        self.plot.getPlotColor = self.getPlotColor

        # BOTTOM RIGHT FRAME

        frame = Frame(outerFrame)
        frame.grid(row=1, column=0, sticky='nsew')
        frame.rowconfigure(0, weight=1)
        frame.columnconfigure(0, weight=1)

        texts = ('Previous', '  Next  ')
        commands = (self.showPrevious, self.showNext)
        tipTexts = [
            'Show GLE plot of angle predictions for previous residue in chain',
            'Show GLE plot of angle predictions for next residue in chain'
        ]
        buttonList = ButtonList(frame, texts, commands, tipTexts=tipTexts)
        buttonList.grid(row=0, column=0, sticky='nsew')

        row += 1

        # BOTTOM LEFT FRAME

        frame = LabelFrame(self, text='Dihedral Angle Predictions')
        frame.grid(row=row, column=0, sticky='nsew')
        frame.grid_columnconfigure(0, weight=1)
        frame.grid_rowconfigure(0, weight=1)

        self.floatEntry = FloatEntry(self,
                                     text='',
                                     returnCallback=self.setFloatEntry,
                                     width=10,
                                     formatPlaces=9)

        tipTexts = [
            'Residue number in chain', 'Residue type code',
            'Number of high scoring discontinuous angle predictions',
            'Predicted secondary structure code',
            'Predicted phi dihedral angle (CO-N-CA-CO)',
            'Predicted psi dihedral angle (N-CA-CO-N)',
            'Upper bound of phi angle prediction',
            'Lower bound of phi angle prediction',
            'Upper bound of psi angle prediction',
            'Lower bound of phi angle prediction',
            'Chemical shifts used in prediction'
        ]

        headingList = [
            'Res\nNum', 'Res\nType', 'No. of\nIslands', 'SS', 'Phi', 'Psi',
            'Phi\nUpper', 'Phi\nLower', 'Psi\nUpper', 'Psi\nLower',
            'Chemical Shifts'
        ]

        editWidgets = [
            None, None, None, None, self.floatEntry, self.floatEntry,
            self.floatEntry, self.floatEntry, self.floatEntry, self.floatEntry
        ]

        editGetCallbacks = [
            None, None, None, None, self.getFloatEntry, self.getFloatEntry,
            self.getFloatEntry, self.getFloatEntry, self.getFloatEntry,
            self.getFloatEntry
        ]

        editSetCallbacks = [
            None, None, None, None, self.setFloatEntry, self.setFloatEntry,
            self.setFloatEntry, self.setFloatEntry, self.setFloatEntry,
            self.setFloatEntry
        ]

        self.predictionMatrix = ScrolledMatrix(
            frame,
            headingList=headingList,
            multiSelect=True,
            callback=self.selectCell,
            tipTexts=tipTexts,
            editWidgets=editWidgets,
            editGetCallbacks=editGetCallbacks,
            editSetCallbacks=editSetCallbacks)
        #                                       doubleCallback=self.loadGLEs)
        self.predictionMatrix.grid(row=0, column=0, sticky='nsew')

        row += 1

        tipTexts = [
            'Remove the predictions for the selected residues',
            'Run the DANGLE method to predict dihedral angles and secondary structure',
            'Delete the DANGLE results stored under the current run number',
            'Store the angle predictions and bounds in a new CCPN dihedral angle restraint list',
            'Store the secondary structure predictions in the CCPN project'
        ]

        texts = [
            'Clear\nSelected', 'Run Prediction!', 'Delete\nCurrent Run',
            'Commit\nRestraints', 'Commit\nSecondary Structure'
        ]
        commands = [
            self.clearSelected, self.runDangle, self.deleteRun,
            self.storeDihedralConstraints, self.storeSecondaryStructure
        ]
        self.buttonList = createDismissHelpButtonList(
            self,
            texts=texts,
            commands=commands,  # dismiss_text='Quit',
            dismiss_cmd=self.dangleGui.quit,
            help_url=self.dangleGui.help_url,
            expands=True,
            tipTexts=tipTexts)
        self.buttonList.grid(row=row, column=0, columnspan=2, sticky='ew')
        self.buttonList.buttons[1].config(bg='#C0FFFF')

        self.updateProject(project)

        self.notify(dangleGui.registerNotify)

    def destroy(self):

        self.notify(self.dangleGui.unregisterNotify)
        Frame.destroy(self)

    def notify(self, notifyfunc):

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

        for func in ('__init__', 'delete', 'setName'):
            notifyfunc(self.updateShiftListPulldown, 'ccp.nmr.Nmr.ShiftList',
                       func)

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

        for func in ('__init__', 'delete'):
            notifyfunc(self.updateEnsemblePulldown,
                       'ccp.molecule.MolStructure.StructureEnsemble', func)

    def updateProject(self, project):

        if project:
            self.project = project
            self.nmrProject = project.currentNmrProject or self.project.newNmrProject(
                name=project.name)
            self.updateShiftListPulldown()
            self.updateChainPulldown()
            self.updateDangleStorePulldown()
            self.updateConstrSetPulldown()
            self.updateEnsemblePulldown()
            self.updatePredictionMatrixAfter()

    def makeDangleInput(self, filename, chain, shiftList):

        if (not chain) or (not shiftList):
            return

        residues = chain.sortedResidues()

        seq = ''
        for residue in residues:
            if residue.molResidue.chemComp.code1Letter:
                seq += residue.molResidue.chemComp.code1Letter
            else:
                seq += 'X'

        res_0 = residues[0].seqId

        fopen = open(filename, 'w')
        fopen.write('<entry>\n')
        fopen.write('\t<res_0>%d</res_0>\n' % res_0)
        fopen.write('\t<seq_1>%s</seq_1>\n' % seq)
        fopen.write('\t<chain>%s</chain>\n' % chain.code)
        fopen.write('\t<cs_data>\n')
        fopen.write('\t\t<!-- res_id  res_name  atom_name  chemical_shift -->')

        numShift = 0

        for residue in residues:
            for atom in residue.atoms:
                atomSet = atom.getAtomSet()
                shifts = getAtomSetShifts(atomSet, shiftList=shiftList)
                if (len(shifts) == 0):
                    continue

                # to average ambiguous chemical shifts ???????????????????????????
                value = 0.
                for shift in shifts:
                    value += shift.value
                value = value / float(len(shifts))

                at_name = atom.name
                res_name = residue.ccpCode
                res_num = residue.seqId
                fopen.write('\n\t\t%5s\t%s\t%-4s\t%.3f' %
                            (res_num, res_name, at_name, value))

                numShift += 1

        fopen.write('\n\t</cs_data>\n')
        fopen.write('</entry>\n')
        fopen.close()

        return numShift

    def deleteRun(self):

        if self.dangleStore:
            msg = 'Really delete DANGLE run "%s"?' % self.dangleStore.name

            if showOkCancel('Confirm', msg, parent=self):
                self.dangleStore.delete()
                self.dangleStore = None
                self.dangleChain = None
                self.updatePredictionMatrix()
                self.updateDangleStorePulldown()

    def runDangle(self):

        chain = self.chain
        shiftList = self.shiftList

        if (not chain) or (not shiftList):
            showError('Cannot Run DANGLE',
                      'Please specify a chain and a shift list.',
                      parent=self)
            return

        # check if there is a DangleChain available
        self.checkDangleStore()
        dangleStore = self.dangleStore

        if not dangleStore:
            return

        dangleChain = dangleStore.findFirstDangleChain(chain=chain)
        if dangleChain:
            data = (chain.code, dangleChain.shiftList.serial)
            msg = 'Predictions for Chain %s using Shift List %d already exist.\nReplace data?' % data

            if not showYesNo('Replace Data', msg, parent=self):
                return

            else:
                self.dangleChain = dangleChain
                dangleChain.shiftList = shiftList

        else:
            self.dangleChain = dangleStore.newDangleChain(chain=chain,
                                                          shiftList=shiftList)

        #dangleStore.packageLocator.repositories[0].url.dataLocation = '/home/msc51/ccpn/NexusTestGI'
        #dangleStore.packageName = 'cambridge.dangle'

        repository = dangleStore.packageLocator.repositories[0]
        array = dangleStore.packageName.split('.')
        path = os.path.join(repository.url.dataLocation, *array)
        path = os.path.join(path, dangleStore.name,
                            chain.code)  # Dangle_dir/dangleStoreName/chainCode
        if not os.path.exists(path):
            os.makedirs(path)

        self.dangleDir = path

        inputFile = os.path.join(self.dangleDir, 'dangle_cs.inp')
        if os.path.isfile(inputFile):
            os.unlink(inputFile)

        outputFile = os.path.join(self.dangleDir, 'danglePred.txt')
        if os.path.isfile(outputFile):
            os.unlink(outputFile)

        numShift = self.makeDangleInput(inputFile, chain, shiftList)

        if not os.path.isfile(inputFile):
            msg = 'No DANGLE input has been generated.\nPlease check shift lists.'
            showError('File Does Not Exist', msg, parent=self)
            return
        if numShift == 0:
            msg = 'No shift data in input file.\nPerhaps shifts are not assigned.\nContinue prediction anyway?'
            if not showYesNo('Empty DANGLE input', msg, parent=self):
                return

        rejectThresh = self.rejectPulldown.getObject()

        # Use the Reference info from the main installation
        # location must be absolute because DANGLE could be run from anywhere
        location = os.path.dirname(dangleModule.__file__)

        progressBar = ProgressBar(self)
        self.update_idletasks()

        dangle = Dangle(location,
                        inputFile=inputFile,
                        outputDir=self.dangleDir,
                        reject=rejectThresh,
                        angleOnly=False,
                        progressBar=progressBar,
                        writePgm=False)

        #self.dangleDir = '/home/msc51/nexus/gItest/DanglePred/'
        #outputFile =  '/home/msc51/nexus/gItest/DanglePred/danglePred.txt'

        predictions = dangle.predictor.predictions
        gleScores = dangle.predictor.gleScores

        self.readPredictions(predictions, gleScores)
        self.updatePredictionMatrix()

    def readPredictions(self, predictions, gleScores):

        progressBar = ProgressBar(self, text='Reading DANGLE predictions')
        progressBar.total = len(predictions) - 2  # 2 header lines

        residues = self.dangleChain.chain.sortedResidues()
        getDangleResidue = self.dangleChain.findFirstDangleResidue
        newDangleResidue = self.dangleChain.newDangleResidue

        for residue in residues:
            seqId = residue.seqId
            prediction = predictions.get(seqId)

            if prediction is None:
                continue

            gleMatrix = gleScores[seqId]

            progressBar.increment()

            #resNum, resName = prediction[:2];

            numIsland = prediction[2]
            ss = prediction[10]
            angles = [min(179.9999, a) for a in prediction[3:10]]

            phi, phiUpper, phiLower, psi, psiUpper, psiLower, omega = angles

            # Normalise to max
            maxVal = max(gleMatrix)
            gleMatrix = [
                max(0, int(val / maxVal * 65535)) / 65535.0
                for val in gleMatrix
            ]

            dangleResidue = getDangleResidue(residue=residue)
            if not dangleResidue:
                dangleResidue = newDangleResidue(
                    phiPsiLikelihoodMatrix=gleMatrix, residue=residue)

            else:
                dangleResidue.phiPsiLikelihoodMatrix = gleMatrix

            dangleResidue.numIslands = numIsland
            dangleResidue.phiValue = phi
            dangleResidue.phiUpper = phiUpper
            dangleResidue.phiLower = phiLower
            dangleResidue.psiValue = psi
            dangleResidue.psiUpper = psiUpper
            dangleResidue.psiLower = psiLower
            dangleResidue.omegaValue = omega
            dangleResidue.secStrucCode = ss

        progressBar.destroy()

    def readPredictionFile(self, filename, chain):

        try:
            fopen = open(filename, 'r')
        except:
            showError('File Reading Error',
                      'DANGLE prediction file %s cannot be open.' % filename,
                      parent=self)
            return

        lines = fopen.readlines()
        progressBar = ProgressBar(self, text='Reading DANGLE predictions')
        progressBar.total = len(lines) - 2  # 2 header lines
        lines = lines[2:]

        for line in lines:
            progressBar.increment()
            if (line == '\n'):
                continue
            array = line.split()  # keep everything as string
            resNum = int(array[0])
            resName = array[1]
            numIsland = int(array[2])
            phi = array[3]
            phiUpper = array[4]
            phiLower = array[5]
            psi = array[6]
            psiUpper = array[7]
            psiLower = array[8]
            omega = array[9]
            ss = array[10]

            if (phi == 'None'):
                phi = None
            else:
                phi = float(phi)
            if (psi == 'None'):
                psi = None
            else:
                psi = float(psi)
            if (omega == 'None'):
                omega = None
            else:
                omega = float(omega)
                if omega == 180:
                    omega = 179.9
            if (phiUpper == 'None'):
                phiUpper = None
            else:
                phiUpper = float(phiUpper)
            if (phiLower == 'None'):
                phiLower = None
            else:
                phiLower = float(phiLower)
            if (psiUpper == 'None'):
                psiUpper = None
            else:
                psiUpper = float(psiUpper)
            if (psiLower == 'None'):
                psiLower = None
            else:
                psiLower = float(psiLower)
            if (ss == 'None'):
                ss = None

            path = os.path.join(self.dangleDir, 'Res_%d.pgm' % resNum)
            gleMatrix = self.readGLE(path)
            residue = chain.findFirstResidue(seqId=int(resNum))

            dangleResidue = self.dangleChain.findFirstDangleResidue(
                residue=residue)
            if not dangleResidue:
                dangleResidue = self.dangleChain.newDangleResidue(
                    phiPsiLikelihoodMatrix=gleMatrix, residue=residue)
            else:
                dangleResidue.phiPsiLikelihoodMatrix = gleMatrix

            dangleResidue.numIslands = numIsland
            dangleResidue.phiValue = phi
            dangleResidue.phiUpper = phiUpper
            dangleResidue.phiLower = phiLower
            dangleResidue.psiValue = psi
            dangleResidue.psiUpper = psiUpper
            dangleResidue.psiLower = psiLower
            dangleResidue.omegaValue = omega
            dangleResidue.secStrucCode = ss

            # Delete temp pgm files to save space once data is in CCPN
            os.unlink(path)

        fopen.close()
        progressBar.destroy()

    def readGLE(self, gleFile):

        if not os.path.isfile(gleFile):
            msg = 'No scorogram Res_%d.pgm\nin directory %s.' % (
                resNum, self.dangleDir)
            showError('File Reading Error', msg, parent=self)
            return None

        fopen = open(gleFile, 'r')
        lines = fopen.readlines()
        dims = lines[2].split()
        lines = lines[4:]
        fopen.close()

        # only read the top left corner of a 10X10 square bin
        # all readings in the same bin are identical
        binSize = 10
        matrix = []
        for j in range(36):
            x = j * binSize * binSize * 36
            for i in range(36):
                y = i * binSize
                v = int(lines[x + y].strip())
                matrix.append(v)

        maxVal = float(max(matrix))
        for i in range(len(matrix)):
            matrix[i] = matrix[i] / maxVal

        return matrix

    def getPhiPsiPredictions(self):

        #if self.dangleChain:
        # dResidues = self.dangleChain.dangleResidues

        dResidues = self.predictionMatrix.objectList

        phiData = []
        psiData = []
        for dResidue in dResidues:
            resNum = dResidue.residue.seqCode
            phi = dResidue.phiValue
            psi = dResidue.psiValue
            phiData.append((resNum, phi))
            psiData.append((resNum, psi))

        return (phiData, psiData)

    def clearSelected(self):

        for dangleResidue in self.predictionMatrix.currentObjects:
            dangleResidue.numIslands = None
            dangleResidue.phiValue = None
            dangleResidue.psiValue = None
            dangleResidue.omegaValue = None
            dangleResidue.phiUpper = None
            dangleResidue.phiLower = None
            dangleResidue.psiUpper = None
            dangleResidue.psiLower = None
            dangleResidue.secStrucCode = None

        self.updatePredictionMatrixAfter()

    def storeSecondaryStructure(self):

        if not self.dangleChain:
            return

        getSpinSystem = self.nmrProject.findFirstResonanceGroup
        newSpinSystem = self.nmrProject.newResonanceGroup

        n = 0
        for dangleResidue in self.dangleChain.dangleResidues:
            ssCode = dangleResidue.secStrucCode

            if not ssCode:
                continue

            residue = dangleResidue.residue
            if not residue:
                continue

            spinSystem = getSpinSystem(residue=residue)

            if not spinSystem:
                spinSystem = newSpinSystem(residue=residue,
                                           ccpCode=residue.ccpCode)

            spinSystem.secStrucCode = ssCode
            n += 1

        showInfo('Info',
                 'Stored secondary structure types for %d residues.' % n,
                 parent=self)

    def storeDihedralConstraints(self):

        if not self.dangleChain:
            return

        # make a new dihedralConstraintList
        head = self.constraintSet

        if not head:
            head = self.project.newNmrConstraintStore(
                nmrProject=self.nmrProject)
            self.constraintSet = head

        chain = self.dangleChain.chain
        shiftList = self.dangleChain.shiftList
        name = 'DANGLE Chain %s:%s ShiftList %d' % (
            chain.molSystem.code, chain.code, shiftList.serial)
        constraintList = head.newDihedralConstraintList(name=name,
                                                        measureListSerials=[
                                                            shiftList.serial,
                                                        ])

        # traverse the sequence and make appropriate constraint objects
        residues = chain.sortedResidues()
        for residue in residues:
            # Ensure we have atomSets etc
            getResidueMapping(residue)

        residueList = [(dr.residue.seqCode, dr.residue, dr)
                       for dr in self.dangleChain.dangleResidues]
        residueList.sort()

        cnt = 0

        for seqCode, residue, dangleResidue in residueList:

            phi = dangleResidue.phiValue
            psi = dangleResidue.psiValue

            if (phi is None) and (psi is None):
                continue

            # Use below functions because residues may not be sequentially numbered
            prevRes = getLinkedResidue(residue, 'prev')
            nextRes = getLinkedResidue(residue, 'next')
            if (prevRes is None) or (nextRes is None):
                continue

            C__1 = prevRes.findFirstAtom(name='C')  # C (i-1)
            N_0 = residue.findFirstAtom(name='N')  # N (i)
            CA_0 = residue.findFirstAtom(name='CA')  # CA(i)
            C_0 = residue.findFirstAtom(name='C')  # N (i)
            N_1 = nextRes.findFirstAtom(name='N')  # C (i+1)

            # get fixedResonances
            fixedResonances = []
            for atom in (C__1, N_0, CA_0, C_0, N_1):
                atomSet = atom.atomSet

                if atomSet.resonanceSets:
                    resonance = atomSet.findFirstResonanceSet(
                    ).findFirstResonance()

                else:
                    # make new resonance
                    if not atom.chemAtom:
                        print 'no chem atom'

                    ic = atom.chemAtom.elementSymbol
                    if (ic == 'C'):
                        ic = '13' + ic
                    elif (ic == 'N'):
                        ic = '15' + ic

                    resonance = self.nmrProject.newResonance(isotopeCode=ic)
                    assignAtomsToRes([
                        atomSet,
                    ], resonance)
                fixedResonances.append(getFixedResonance(head, resonance))

            # make dihedralConstraints
            phiResonances = (fixedResonances[0], fixedResonances[1],
                             fixedResonances[2], fixedResonances[3])
            phiConstraint = constraintList.newDihedralConstraint(
                resonances=phiResonances)

            psiResonances = (fixedResonances[1], fixedResonances[2],
                             fixedResonances[3], fixedResonances[4])
            psiConstraint = constraintList.newDihedralConstraint(
                resonances=psiResonances)

            # make constraint items

            if phi is not None:
                phiConstraint.newDihedralConstraintItem(
                    targetValue=phi,
                    upperLimit=dangleResidue.phiUpper,
                    lowerLimit=dangleResidue.phiLower)
                cnt += 1

            if psi is not None:
                psiConstraint.newDihedralConstraintItem(
                    targetValue=psi,
                    upperLimit=dangleResidue.psiUpper,
                    lowerLimit=dangleResidue.psiLower)
                cnt += 1

        showInfo('Success',
                 'DANGLE has generated %d dihedral restraints.' % cnt,
                 parent=self)

    def loadGLEs(self, dRes, row, col):

        residue = dRes.residue
        title = '%d %s' % (residue.seqCode, residue.ccpCode)

        self.fillGlePlot(self.plot, dRes.phiPsiLikelihoodMatrix, title)

        prevDangleRes = self.getDangleResidue(dRes, 'prev')
        if prevDangleRes:
            self.fillGlePlot(self.prevPlot,
                             prevDangleRes.phiPsiLikelihoodMatrix)
        else:
            self.fillGlePlot(self.prevPlot, [0] * 1296)  # blank

        nextDangleRes = self.getDangleResidue(dRes, 'next')
        if nextDangleRes:
            self.fillGlePlot(self.nextPlot,
                             nextDangleRes.phiPsiLikelihoodMatrix)
        else:
            self.fillGlePlot(self.nextPlot, [0] * 1296)  # blank

        self.updatePhiPsi(dRes.residue)

    def fillGlePlot(self, plot, gleMatrix, title=None):

        scaleCol = plot.scaleColorQuick

        if self.colorScheme == 'black':
            plot.nullColor = '#000000'
        else:
            plot.nullColor = '#FFFFFF'

        itemconf = plot.canvas.itemconfigure
        matrix = plot.matrix

        for j in range(36):
            for i in range(36):
                v = gleMatrix[j * 36 + i]
                #if (v < 0.005):
                #  color = plot.nullColor
                #else:
                color = self.getPlotColor(v)
                item = matrix[i][j]

                if plot.binWidth < 7:
                    itemconf(item, fill=color, outline=color)
                elif plot.binWidth < 12:
                    itemconf(item, fill=color, outline=scaleCol(color, 0.9))
                else:
                    itemconf(item, fill=color, outline=scaleCol(color, 0.8))

        if title:
            itemconf(plot.title, text=title)

    def getDangleResidue(self, dRes, direction):
        # return a DangleResidue object located offset-residue away from dRes in sequence

        # Use below function to guard against non-sequentially numbered residues
        # the below function follows bonds, but uses a cache for speed
        residue = getLinkedResidue(dRes.residue, direction)

        if residue and self.dangleChain:
            return self.dangleChain.findFirstDangleResidue(residue=residue)

    def showPrevious(self):

        if not self.dangleResidue:
            return

        prevDangleResidue = self.getDangleResidue(self.dangleResidue, 'prev')
        if not prevDangleResidue:
            return

        self.predictionMatrix.selectObject(prevDangleResidue)
        #self.dangleResidue = prevDangleResidue
        #self.loadGLEs(self.dangleResidue, None, None)
        #self.predictionMatrix.currentObject = self.dangleResidue
        #self.predictionMatrix.hilightObject(self.predictionMatrix.currentObject)

    def showNext(self):

        if not self.dangleResidue:
            return

        nextDangleResidue = self.getDangleResidue(self.dangleResidue, 'next')
        if not nextDangleResidue:
            return

        self.predictionMatrix.selectObject(nextDangleResidue)
        #self.dangleResidue = nextDangleResidue
        #self.loadGLEs(self.dangleResidue, None, None)
        #self.predictionMatrix.currentObject = self.dangleResidue
        #self.predictionMatrix.hilightObject(self.predictionMatrix.currentObject)

    def updatePhiPsi(self, residue):

        if self.ensemble:
            phiPsiAccept = []
            plotObjects = []
            colors = []

            cChain = self.ensemble.findFirstCoordChain(code=residue.chain.code)

            if cChain:
                cResidue = cChain.findFirstResidue(residue=residue)

                if cResidue:
                    for model in self.ensemble.models:
                        phiPsiAccept.append(self.getPhiPsi(cResidue, model))
                        plotObjects.append((cResidue, model))
                        colors.append(ENSEMBLE_COLOR)

            if self.colorScheme == 'rainbow':  # default grey circles
                self.plot.updateObjects(phiPsiAccList=phiPsiAccept,
                                        objectList=plotObjects)
            else:  # bright green circles
                self.plot.updateObjects(phiPsiAccList=phiPsiAccept,
                                        objectList=plotObjects,
                                        colors=colors)

    def getPhiPsi(self, residue, model=None):

        phi, psi = getResiduePhiPsi(residue, model=model)
        return (phi, psi, 1)

    def getPlotColor(self, i, maxInt=255):

        mode = self.colorScheme
        if mode == 'rainbow':
            if (i == 0):
                return '#%02x%02x%02x' % (255, 255, 255)  # white bg
            elif (i > 0.75):
                red = 1
                green = (1 - i) / 0.25
                blue = 0
            elif (i > 0.5):
                red = (i - 0.5) / 0.25
                green = 1
                blue = 0
            elif (i > 0.25):
                red = 0
                green = 1
                blue = (0.5 - i) / 0.25
            else:
                red = 0
                green = i / 0.25
                blue = 1
            return '#%02x%02x%02x' % (red * maxInt, green * maxInt,
                                      blue * maxInt)
        """
    elif mode == 'black':
      if i > 0.5:
        red   = i
        green = 1 - i
        blue  = 1 - i
      else:
        v = 0.1 + (0.9 * i)
        red   = v
        green = v
        blue  = v
        
    elif mode == 'white':
      if i > 0.5:
        red   = i
        green = 1 - i
        blue  = 1 - i
      else:
        v = 1.0 - (0.9 * i)
        red   = v
        green = v
        blue  = v

    return '#%02x%02x%02x' % (red*maxInt, green*maxInt, blue*maxInt)
    """

        # default : red to black

        if (i == 0):
            return '#%02x%02x%02x' % (255, 255, 255)  # white bg

        return '#%02x%02x%02x' % (((1 - i) * 255), 0, 0)

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

        if self.chain and self.shiftList and self.dangleStore:
            self.dangleChain = self.dangleStore.findFirstDangleChain(
                chain=self.chain, shiftList=self.shiftList)
        else:
            self.dangleChain = None

        self.after_idle(self.updatePredictionMatrix)

        #if showYesNo('Not Found','No data for Chain %s in Dangle Run %s.\nMake prediction for this chain?' % (self.chain.code, text), parent=self):
        #  self.runDangle()

    def updatePredictionMatrix(self):

        shiftList = self.shiftList
        objectList = []
        textMatrix = []
        colorMatrix = []

        if self.dangleChain:
            residueList = [(dr.residue.seqCode, dr.residue, dr)
                           for dr in self.dangleChain.dangleResidues]
            residueList.sort()
        else:
            # Chow blank table
            residueList = []

        for seqCode, residue, dRes in residueList:
            objectList.append(dRes)

            phi = dRes.phiValue
            psi = dRes.psiValue
            ss = dRes.secStrucCode

            atomNames = []
            for atomName in BACKBONE_ATOMS:
                atom = residue.findFirstAtom(name=atomName)
                if not atom:
                    continue

                atomSet = atom.atomSet

                if atomSet:
                    shifts = getAtomSetShifts(atomSet, shiftList=shiftList)
                    if shifts:
                        atomNames.append(atomName)

            atomNames.sort()
            atomNames = ' '.join(atomNames)

            textMatrix.append((seqCode, residue.ccpCode, dRes.numIslands, ss,
                               phi, psi, dRes.phiUpper, dRes.phiLower,
                               dRes.psiUpper, dRes.psiLower, atomNames))

            if (phi is None) and (psi is None):
                colorMatrix.append(INACTIVE_COLORS)

            elif dRes.numIslands >= 5:
                colorMatrix.append(BAD_COLORS)

            else:
                colorMatrix.append(GOOD_COLORS)

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

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

        self.dangleResidue = dRes
        self.row = row
        self.col = col
        self.loadGLEs(dRes, row, col)

    def setFloatEntry(self, event):

        index = self.col - 4  # index of attribute to set in the EDIT_ATTRS list
        value = self.floatEntry.get()

        if value is not None:
            setattr(self.dangleResidue, EDIT_ATTRS[index], value)
            self.updatePredictionMatrixAfter()

    def getFloatEntry(self, dangleResidue):

        if dangleResidue:
            index = self.col - 4  # index of attribute to set in the EDIT_ATTRS list
            self.floatEntry.set(getattr(dangleResidue, EDIT_ATTRS[index]))

    def changeChain(self, chain):

        if chain is not self.chain:
            self.chain = chain
            self.updateEnsemblePulldown(
            )  # Ensembles are filtered by chains molSystem
            self.updatePredictionMatrixAfter()

    def changeShiftList(self, shiftList):

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

    def changeEnsemble(self, ensemble):

        self.ensemble = ensemble

    def changeConstraintSet(self, constraintSet):

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

    def changeDangleStore(self, dangleStore):

        if self.dangleStore is not dangleStore:
            self.dangleStore = dangleStore

            if dangleStore:
                self.dangleChain = dangleStore.findFirstDangleChain()
                self.chain = self.dangleChain.chain
                self.shiftList = self.dangleChain.shiftList
            else:
                self.dangleChain = None

            self.updateChainPulldown()
            self.updateShiftListPulldown()
            self.updateEnsemblePulldown(
            )  # Ensembles are filtered by chains molSystem
            self.updatePredictionMatrixAfter()

    def checkDangleStore(self):

        if not self.dangleStore:

            N = len(self.project.dangleStores) + 1
            name = askString('Request',
                             'Dangle Run Name:',
                             'Run%d' % N,
                             parent=self)
            if not name:
                return None

            for character in whitespace:
                if character in name:
                    showWarning('Failure',
                                'Name cannot contain whitespace',
                                parent=self)
                    return None

            if self.project.findFirstDangleStore(name=name):
                showWarning('Failure', 'Name already used', parent=self)
                return None

            self.dangleStore = self.project.newDangleStore(name=name)
            self.updateDangleStorePulldown()

    def updateChainPulldown(self, obj=None):

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

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

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

                if not residues:
                    continue

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

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

            index = chains.index(chain)

        else:
            chain = None

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

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

    def updateShiftListPulldown(self, obj=None):

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

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

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

        else:
            self.shiftList = None

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

    def updateDangleStorePulldown(self):

        names = [
            '<New>',
        ]
        dangleStores = [
            None,
        ]

        for dangleStore in self.project.sortedDangleStores():
            names.append(dangleStore.name)
            dangleStores.append(dangleStore)

        if self.dangleStore not in dangleStores:
            self.dangleStore = dangleStores[-1]

        index = dangleStores.index(self.dangleStore)

        self.dangleStorePulldown.setup(names, dangleStores, index)

    def updateEnsemblePulldown(self, obj=None):

        index = 0
        names = [
            '<None>',
        ]
        ensembles = [
            None,
        ]

        if self.chain:
            molSystem = self.chain.molSystem

            for ensemble in molSystem.sortedStructureEnsembles():
                names.append('%s:%d' % (molSystem.code, ensemble.ensembleId))
                ensembles.append(ensemble)

            if self.ensemble not in ensembles:
                self.ensemble = ensembles[0]

            index = ensembles.index(self.ensemble)

        self.ensemblePulldown.setup(names, ensembles, index)

    def updateConstrSetPulldown(self, obj=None):

        names = [
            '<New>',
        ]
        constraintSets = [
            None,
        ]

        # Use below later, once API speed/loading is improved
        # for constraintSet in self.nmrProject.sortedNmrConstraintStores():

        for constraintSet in self.project.sortedNmrConstraintStores():
            names.append('%d' % constraintSet.serial)
            constraintSets.append(constraintSet)

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

        index = constraintSets.index(self.constraintSet)

        self.constrSetPulldown.setup(names, constraintSets, index)

    def try1(self):

        if not self.project:
            return

        ccpCodes = [
            'Ala', 'Cys', 'Asp', 'Glu', 'Phe', 'Gly', 'His', 'Ile', 'Lys',
            'Leu', 'Met', 'Asn', 'Gln', 'Arg', 'Ser', 'Thr', 'Val', 'Trp',
            'Tyr', 'Pro'
        ]

        atomNames = ['HA', 'CA', 'CB', 'C', 'N']

        molType = 'protein'

        for ccpCode in ccpCodes:
            for atomName in atomNames:

                chemAtomNmrRef = getChemAtomNmrRef(self.project, atomName,
                                                   ccpCode, molType)
                mean = chemAtomNmrRef.meanValue
                sd = chemAtomNmrRef.stdDev

                print '%5s%5s   %.3f   %.3f' % (ccpCode, atomName, mean, sd)
示例#3
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)
示例#4
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