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
class DangleFrame(Frame): def __init__(self, parent, dangleGui, project=None, *args, **kw): self.guiParent = parent self.dangleGui = dangleGui self.dangleDir = None self.dangleChain = None self.dangleResidue = None #self.outDir = OUTDIR self.row = None self.col = None self.project = project self.nmrProject = None self.colorScheme = 'red' self.chain = None self.shiftList = None self.dangleStore = False # Not None self.constraintSet = None self.ensemble = None Frame.__init__(self, parent=parent) self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(1, weight=1) row = 0 # TOP LEFT FRAME frame = LabelFrame(self, text='Options') frame.grid(row=row, column=0, sticky='nsew') frame.columnconfigure(5, weight=1) label = Label(frame, text='Chain') label.grid(row=0, column=0, sticky='w') self.chainPulldown = PulldownList( frame, callback=self.changeChain, tipText='Choose the molecular system chain to make predictions for' ) self.chainPulldown.grid(row=0, column=1, sticky='w') label = Label(frame, text='Shift List') label.grid(row=0, column=2, sticky='w') self.shiftListPulldown = PulldownList( frame, callback=self.changeShiftList, tipText='Select the shift list to take input chemical shifts from') self.shiftListPulldown.grid(row=0, column=3, sticky='w') label = Label(frame, text='Max No. of Islands:') label.grid(row=0, column=4, sticky='w') sizes = range(10) texts = [str(s) for s in sizes] + [ 'Do not reject', ] self.rejectPulldown = PulldownList( frame, texts=texts, objects=sizes + [ None, ], tipText= 'Select the maximum allowed number of disontinuous prediction islands' ) self.rejectPulldown.set(DEFAULT_MAX_ISLANDS) # Actual value not index self.rejectPulldown.grid(row=0, column=5, sticky='w') label = Label(frame, text='Dangle Run:') label.grid(row=1, column=0, sticky='w') self.dangleStorePulldown = PulldownList( frame, callback=self.changeDangleStore, tipText='Select a run number to store DANGLE results within') self.dangleStorePulldown.grid(row=1, column=1, sticky='w') label = Label(frame, text='Ensemble:') label.grid(row=1, column=2, sticky='w') self.ensemblePulldown = PulldownList( frame, callback=self.changeEnsemble, tipText= 'Select the structure ensemble for superimposition of angle values on the GLE plots' ) self.ensemblePulldown.grid(row=1, column=3, sticky='w') label = Label(frame, text='Restraint Set:') label.grid(row=1, column=4, sticky='w') self.constrSetPulldown = PulldownList( frame, callback=self.changeConstraintSet, tipText= 'Select the CCPN restraint set to store DANGLE dihedral angle restraints in' ) self.constrSetPulldown.grid(row=1, column=5, sticky='w') # TOP RIGHT FRAME outerFrame = Frame(self) outerFrame.grid(row=row, column=1, rowspan=2, sticky='nsew') outerFrame.rowconfigure(0, weight=1) outerFrame.columnconfigure(0, weight=1) frame = LabelFrame(outerFrame, text='Global Likelihood Estimates') frame.grid(row=0, column=0, sticky='nsew') frame.rowconfigure(1, weight=1) frame.columnconfigure(2, weight=1) self.prevPlot = ViewRamachandranFrame(frame, relief='sunken', defaultPlot=False, width=180, height=180, bgColor=self.cget('bg'), nullColor='#000000', titleText='Previous', xTicks=False, yTicks=False, xLabel='', yLabel='', showCoords=False) self.prevPlot.grid(row=0, column=0, sticky='nsew') self.prevPlot.getPlotColor = self.getPlotColor self.nextPlot = ViewRamachandranFrame(frame, relief='sunken', defaultPlot=False, width=180, height=180, bgColor=self.cget('bg'), nullColor='#000000', titleText='Next', xTicks=False, yTicks=False, xLabel='', yLabel='', showCoords=False) self.nextPlot.grid(row=0, column=1, sticky='nsew') self.nextPlot.getPlotColor = self.getPlotColor self.plot = ViewRamachandranFrame(frame, relief='sunken', defaultPlot=False, width=360, height=360, bgColor=self.cget('bg'), nullColor='#000000') self.plot.grid(row=1, column=0, columnspan=2, sticky='nsew') self.plot.selectColor = '#FFB0B0' self.plot.getPlotColor = self.getPlotColor # BOTTOM RIGHT FRAME frame = Frame(outerFrame) frame.grid(row=1, column=0, sticky='nsew') frame.rowconfigure(0, weight=1) frame.columnconfigure(0, weight=1) texts = ('Previous', ' Next ') commands = (self.showPrevious, self.showNext) tipTexts = [ 'Show GLE plot of angle predictions for previous residue in chain', 'Show GLE plot of angle predictions for next residue in chain' ] buttonList = ButtonList(frame, texts, commands, tipTexts=tipTexts) buttonList.grid(row=0, column=0, sticky='nsew') row += 1 # BOTTOM LEFT FRAME frame = LabelFrame(self, text='Dihedral Angle Predictions') frame.grid(row=row, column=0, sticky='nsew') frame.grid_columnconfigure(0, weight=1) frame.grid_rowconfigure(0, weight=1) self.floatEntry = FloatEntry(self, text='', returnCallback=self.setFloatEntry, width=10, formatPlaces=9) tipTexts = [ 'Residue number in chain', 'Residue type code', 'Number of high scoring discontinuous angle predictions', 'Predicted secondary structure code', 'Predicted phi dihedral angle (CO-N-CA-CO)', 'Predicted psi dihedral angle (N-CA-CO-N)', 'Upper bound of phi angle prediction', 'Lower bound of phi angle prediction', 'Upper bound of psi angle prediction', 'Lower bound of phi angle prediction', 'Chemical shifts used in prediction' ] headingList = [ 'Res\nNum', 'Res\nType', 'No. of\nIslands', 'SS', 'Phi', 'Psi', 'Phi\nUpper', 'Phi\nLower', 'Psi\nUpper', 'Psi\nLower', 'Chemical Shifts' ] editWidgets = [ None, None, None, None, self.floatEntry, self.floatEntry, self.floatEntry, self.floatEntry, self.floatEntry, self.floatEntry ] editGetCallbacks = [ None, None, None, None, self.getFloatEntry, self.getFloatEntry, self.getFloatEntry, self.getFloatEntry, self.getFloatEntry, self.getFloatEntry ] editSetCallbacks = [ None, None, None, None, self.setFloatEntry, self.setFloatEntry, self.setFloatEntry, self.setFloatEntry, self.setFloatEntry, self.setFloatEntry ] self.predictionMatrix = ScrolledMatrix( frame, headingList=headingList, multiSelect=True, callback=self.selectCell, tipTexts=tipTexts, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks) # doubleCallback=self.loadGLEs) self.predictionMatrix.grid(row=0, column=0, sticky='nsew') row += 1 tipTexts = [ 'Remove the predictions for the selected residues', 'Run the DANGLE method to predict dihedral angles and secondary structure', 'Delete the DANGLE results stored under the current run number', 'Store the angle predictions and bounds in a new CCPN dihedral angle restraint list', 'Store the secondary structure predictions in the CCPN project' ] texts = [ 'Clear\nSelected', 'Run Prediction!', 'Delete\nCurrent Run', 'Commit\nRestraints', 'Commit\nSecondary Structure' ] commands = [ self.clearSelected, self.runDangle, self.deleteRun, self.storeDihedralConstraints, self.storeSecondaryStructure ] self.buttonList = createDismissHelpButtonList( self, texts=texts, commands=commands, # dismiss_text='Quit', dismiss_cmd=self.dangleGui.quit, help_url=self.dangleGui.help_url, expands=True, tipTexts=tipTexts) self.buttonList.grid(row=row, column=0, columnspan=2, sticky='ew') self.buttonList.buttons[1].config(bg='#C0FFFF') self.updateProject(project) self.notify(dangleGui.registerNotify) def destroy(self): self.notify(self.dangleGui.unregisterNotify) Frame.destroy(self) def notify(self, notifyfunc): for func in ('__init__', 'delete'): notifyfunc(self.updateChainPulldown, 'ccp.molecule.MolSystem.Chain', func) for func in ('__init__', 'delete', 'setName'): notifyfunc(self.updateShiftListPulldown, 'ccp.nmr.Nmr.ShiftList', func) for func in ('__init__', 'delete'): notifyfunc(self.updateConstrSetPulldown, 'ccp.nmr.NmrConstraint.NmrConstraintStore', func) for func in ('__init__', 'delete'): notifyfunc(self.updateEnsemblePulldown, 'ccp.molecule.MolStructure.StructureEnsemble', func) def updateProject(self, project): if project: self.project = project self.nmrProject = project.currentNmrProject or self.project.newNmrProject( name=project.name) self.updateShiftListPulldown() self.updateChainPulldown() self.updateDangleStorePulldown() self.updateConstrSetPulldown() self.updateEnsemblePulldown() self.updatePredictionMatrixAfter() def makeDangleInput(self, filename, chain, shiftList): if (not chain) or (not shiftList): return residues = chain.sortedResidues() seq = '' for residue in residues: if residue.molResidue.chemComp.code1Letter: seq += residue.molResidue.chemComp.code1Letter else: seq += 'X' res_0 = residues[0].seqId fopen = open(filename, 'w') fopen.write('<entry>\n') fopen.write('\t<res_0>%d</res_0>\n' % res_0) fopen.write('\t<seq_1>%s</seq_1>\n' % seq) fopen.write('\t<chain>%s</chain>\n' % chain.code) fopen.write('\t<cs_data>\n') fopen.write('\t\t<!-- res_id res_name atom_name chemical_shift -->') numShift = 0 for residue in residues: for atom in residue.atoms: atomSet = atom.getAtomSet() shifts = getAtomSetShifts(atomSet, shiftList=shiftList) if (len(shifts) == 0): continue # to average ambiguous chemical shifts ??????????????????????????? value = 0. for shift in shifts: value += shift.value value = value / float(len(shifts)) at_name = atom.name res_name = residue.ccpCode res_num = residue.seqId fopen.write('\n\t\t%5s\t%s\t%-4s\t%.3f' % (res_num, res_name, at_name, value)) numShift += 1 fopen.write('\n\t</cs_data>\n') fopen.write('</entry>\n') fopen.close() return numShift def deleteRun(self): if self.dangleStore: msg = 'Really delete DANGLE run "%s"?' % self.dangleStore.name if showOkCancel('Confirm', msg, parent=self): self.dangleStore.delete() self.dangleStore = None self.dangleChain = None self.updatePredictionMatrix() self.updateDangleStorePulldown() def runDangle(self): chain = self.chain shiftList = self.shiftList if (not chain) or (not shiftList): showError('Cannot Run DANGLE', 'Please specify a chain and a shift list.', parent=self) return # check if there is a DangleChain available self.checkDangleStore() dangleStore = self.dangleStore if not dangleStore: return dangleChain = dangleStore.findFirstDangleChain(chain=chain) if dangleChain: data = (chain.code, dangleChain.shiftList.serial) msg = 'Predictions for Chain %s using Shift List %d already exist.\nReplace data?' % data if not showYesNo('Replace Data', msg, parent=self): return else: self.dangleChain = dangleChain dangleChain.shiftList = shiftList else: self.dangleChain = dangleStore.newDangleChain(chain=chain, shiftList=shiftList) #dangleStore.packageLocator.repositories[0].url.dataLocation = '/home/msc51/ccpn/NexusTestGI' #dangleStore.packageName = 'cambridge.dangle' repository = dangleStore.packageLocator.repositories[0] array = dangleStore.packageName.split('.') path = os.path.join(repository.url.dataLocation, *array) path = os.path.join(path, dangleStore.name, chain.code) # Dangle_dir/dangleStoreName/chainCode if not os.path.exists(path): os.makedirs(path) self.dangleDir = path inputFile = os.path.join(self.dangleDir, 'dangle_cs.inp') if os.path.isfile(inputFile): os.unlink(inputFile) outputFile = os.path.join(self.dangleDir, 'danglePred.txt') if os.path.isfile(outputFile): os.unlink(outputFile) numShift = self.makeDangleInput(inputFile, chain, shiftList) if not os.path.isfile(inputFile): msg = 'No DANGLE input has been generated.\nPlease check shift lists.' showError('File Does Not Exist', msg, parent=self) return if numShift == 0: msg = 'No shift data in input file.\nPerhaps shifts are not assigned.\nContinue prediction anyway?' if not showYesNo('Empty DANGLE input', msg, parent=self): return rejectThresh = self.rejectPulldown.getObject() # Use the Reference info from the main installation # location must be absolute because DANGLE could be run from anywhere location = os.path.dirname(dangleModule.__file__) progressBar = ProgressBar(self) self.update_idletasks() dangle = Dangle(location, inputFile=inputFile, outputDir=self.dangleDir, reject=rejectThresh, angleOnly=False, progressBar=progressBar, writePgm=False) #self.dangleDir = '/home/msc51/nexus/gItest/DanglePred/' #outputFile = '/home/msc51/nexus/gItest/DanglePred/danglePred.txt' predictions = dangle.predictor.predictions gleScores = dangle.predictor.gleScores self.readPredictions(predictions, gleScores) self.updatePredictionMatrix() def readPredictions(self, predictions, gleScores): progressBar = ProgressBar(self, text='Reading DANGLE predictions') progressBar.total = len(predictions) - 2 # 2 header lines residues = self.dangleChain.chain.sortedResidues() getDangleResidue = self.dangleChain.findFirstDangleResidue newDangleResidue = self.dangleChain.newDangleResidue for residue in residues: seqId = residue.seqId prediction = predictions.get(seqId) if prediction is None: continue gleMatrix = gleScores[seqId] progressBar.increment() #resNum, resName = prediction[:2]; numIsland = prediction[2] ss = prediction[10] angles = [min(179.9999, a) for a in prediction[3:10]] phi, phiUpper, phiLower, psi, psiUpper, psiLower, omega = angles # Normalise to max maxVal = max(gleMatrix) gleMatrix = [ max(0, int(val / maxVal * 65535)) / 65535.0 for val in gleMatrix ] dangleResidue = getDangleResidue(residue=residue) if not dangleResidue: dangleResidue = newDangleResidue( phiPsiLikelihoodMatrix=gleMatrix, residue=residue) else: dangleResidue.phiPsiLikelihoodMatrix = gleMatrix dangleResidue.numIslands = numIsland dangleResidue.phiValue = phi dangleResidue.phiUpper = phiUpper dangleResidue.phiLower = phiLower dangleResidue.psiValue = psi dangleResidue.psiUpper = psiUpper dangleResidue.psiLower = psiLower dangleResidue.omegaValue = omega dangleResidue.secStrucCode = ss progressBar.destroy() def readPredictionFile(self, filename, chain): try: fopen = open(filename, 'r') except: showError('File Reading Error', 'DANGLE prediction file %s cannot be open.' % filename, parent=self) return lines = fopen.readlines() progressBar = ProgressBar(self, text='Reading DANGLE predictions') progressBar.total = len(lines) - 2 # 2 header lines lines = lines[2:] for line in lines: progressBar.increment() if (line == '\n'): continue array = line.split() # keep everything as string resNum = int(array[0]) resName = array[1] numIsland = int(array[2]) phi = array[3] phiUpper = array[4] phiLower = array[5] psi = array[6] psiUpper = array[7] psiLower = array[8] omega = array[9] ss = array[10] if (phi == 'None'): phi = None else: phi = float(phi) if (psi == 'None'): psi = None else: psi = float(psi) if (omega == 'None'): omega = None else: omega = float(omega) if omega == 180: omega = 179.9 if (phiUpper == 'None'): phiUpper = None else: phiUpper = float(phiUpper) if (phiLower == 'None'): phiLower = None else: phiLower = float(phiLower) if (psiUpper == 'None'): psiUpper = None else: psiUpper = float(psiUpper) if (psiLower == 'None'): psiLower = None else: psiLower = float(psiLower) if (ss == 'None'): ss = None path = os.path.join(self.dangleDir, 'Res_%d.pgm' % resNum) gleMatrix = self.readGLE(path) residue = chain.findFirstResidue(seqId=int(resNum)) dangleResidue = self.dangleChain.findFirstDangleResidue( residue=residue) if not dangleResidue: dangleResidue = self.dangleChain.newDangleResidue( phiPsiLikelihoodMatrix=gleMatrix, residue=residue) else: dangleResidue.phiPsiLikelihoodMatrix = gleMatrix dangleResidue.numIslands = numIsland dangleResidue.phiValue = phi dangleResidue.phiUpper = phiUpper dangleResidue.phiLower = phiLower dangleResidue.psiValue = psi dangleResidue.psiUpper = psiUpper dangleResidue.psiLower = psiLower dangleResidue.omegaValue = omega dangleResidue.secStrucCode = ss # Delete temp pgm files to save space once data is in CCPN os.unlink(path) fopen.close() progressBar.destroy() def readGLE(self, gleFile): if not os.path.isfile(gleFile): msg = 'No scorogram Res_%d.pgm\nin directory %s.' % ( resNum, self.dangleDir) showError('File Reading Error', msg, parent=self) return None fopen = open(gleFile, 'r') lines = fopen.readlines() dims = lines[2].split() lines = lines[4:] fopen.close() # only read the top left corner of a 10X10 square bin # all readings in the same bin are identical binSize = 10 matrix = [] for j in range(36): x = j * binSize * binSize * 36 for i in range(36): y = i * binSize v = int(lines[x + y].strip()) matrix.append(v) maxVal = float(max(matrix)) for i in range(len(matrix)): matrix[i] = matrix[i] / maxVal return matrix def getPhiPsiPredictions(self): #if self.dangleChain: # dResidues = self.dangleChain.dangleResidues dResidues = self.predictionMatrix.objectList phiData = [] psiData = [] for dResidue in dResidues: resNum = dResidue.residue.seqCode phi = dResidue.phiValue psi = dResidue.psiValue phiData.append((resNum, phi)) psiData.append((resNum, psi)) return (phiData, psiData) def clearSelected(self): for dangleResidue in self.predictionMatrix.currentObjects: dangleResidue.numIslands = None dangleResidue.phiValue = None dangleResidue.psiValue = None dangleResidue.omegaValue = None dangleResidue.phiUpper = None dangleResidue.phiLower = None dangleResidue.psiUpper = None dangleResidue.psiLower = None dangleResidue.secStrucCode = None self.updatePredictionMatrixAfter() def storeSecondaryStructure(self): if not self.dangleChain: return getSpinSystem = self.nmrProject.findFirstResonanceGroup newSpinSystem = self.nmrProject.newResonanceGroup n = 0 for dangleResidue in self.dangleChain.dangleResidues: ssCode = dangleResidue.secStrucCode if not ssCode: continue residue = dangleResidue.residue if not residue: continue spinSystem = getSpinSystem(residue=residue) if not spinSystem: spinSystem = newSpinSystem(residue=residue, ccpCode=residue.ccpCode) spinSystem.secStrucCode = ssCode n += 1 showInfo('Info', 'Stored secondary structure types for %d residues.' % n, parent=self) def storeDihedralConstraints(self): if not self.dangleChain: return # make a new dihedralConstraintList head = self.constraintSet if not head: head = self.project.newNmrConstraintStore( nmrProject=self.nmrProject) self.constraintSet = head chain = self.dangleChain.chain shiftList = self.dangleChain.shiftList name = 'DANGLE Chain %s:%s ShiftList %d' % ( chain.molSystem.code, chain.code, shiftList.serial) constraintList = head.newDihedralConstraintList(name=name, measureListSerials=[ shiftList.serial, ]) # traverse the sequence and make appropriate constraint objects residues = chain.sortedResidues() for residue in residues: # Ensure we have atomSets etc getResidueMapping(residue) residueList = [(dr.residue.seqCode, dr.residue, dr) for dr in self.dangleChain.dangleResidues] residueList.sort() cnt = 0 for seqCode, residue, dangleResidue in residueList: phi = dangleResidue.phiValue psi = dangleResidue.psiValue if (phi is None) and (psi is None): continue # Use below functions because residues may not be sequentially numbered prevRes = getLinkedResidue(residue, 'prev') nextRes = getLinkedResidue(residue, 'next') if (prevRes is None) or (nextRes is None): continue C__1 = prevRes.findFirstAtom(name='C') # C (i-1) N_0 = residue.findFirstAtom(name='N') # N (i) CA_0 = residue.findFirstAtom(name='CA') # CA(i) C_0 = residue.findFirstAtom(name='C') # N (i) N_1 = nextRes.findFirstAtom(name='N') # C (i+1) # get fixedResonances fixedResonances = [] for atom in (C__1, N_0, CA_0, C_0, N_1): atomSet = atom.atomSet if atomSet.resonanceSets: resonance = atomSet.findFirstResonanceSet( ).findFirstResonance() else: # make new resonance if not atom.chemAtom: print 'no chem atom' ic = atom.chemAtom.elementSymbol if (ic == 'C'): ic = '13' + ic elif (ic == 'N'): ic = '15' + ic resonance = self.nmrProject.newResonance(isotopeCode=ic) assignAtomsToRes([ atomSet, ], resonance) fixedResonances.append(getFixedResonance(head, resonance)) # make dihedralConstraints phiResonances = (fixedResonances[0], fixedResonances[1], fixedResonances[2], fixedResonances[3]) phiConstraint = constraintList.newDihedralConstraint( resonances=phiResonances) psiResonances = (fixedResonances[1], fixedResonances[2], fixedResonances[3], fixedResonances[4]) psiConstraint = constraintList.newDihedralConstraint( resonances=psiResonances) # make constraint items if phi is not None: phiConstraint.newDihedralConstraintItem( targetValue=phi, upperLimit=dangleResidue.phiUpper, lowerLimit=dangleResidue.phiLower) cnt += 1 if psi is not None: psiConstraint.newDihedralConstraintItem( targetValue=psi, upperLimit=dangleResidue.psiUpper, lowerLimit=dangleResidue.psiLower) cnt += 1 showInfo('Success', 'DANGLE has generated %d dihedral restraints.' % cnt, parent=self) def loadGLEs(self, dRes, row, col): residue = dRes.residue title = '%d %s' % (residue.seqCode, residue.ccpCode) self.fillGlePlot(self.plot, dRes.phiPsiLikelihoodMatrix, title) prevDangleRes = self.getDangleResidue(dRes, 'prev') if prevDangleRes: self.fillGlePlot(self.prevPlot, prevDangleRes.phiPsiLikelihoodMatrix) else: self.fillGlePlot(self.prevPlot, [0] * 1296) # blank nextDangleRes = self.getDangleResidue(dRes, 'next') if nextDangleRes: self.fillGlePlot(self.nextPlot, nextDangleRes.phiPsiLikelihoodMatrix) else: self.fillGlePlot(self.nextPlot, [0] * 1296) # blank self.updatePhiPsi(dRes.residue) def fillGlePlot(self, plot, gleMatrix, title=None): scaleCol = plot.scaleColorQuick if self.colorScheme == 'black': plot.nullColor = '#000000' else: plot.nullColor = '#FFFFFF' itemconf = plot.canvas.itemconfigure matrix = plot.matrix for j in range(36): for i in range(36): v = gleMatrix[j * 36 + i] #if (v < 0.005): # color = plot.nullColor #else: color = self.getPlotColor(v) item = matrix[i][j] if plot.binWidth < 7: itemconf(item, fill=color, outline=color) elif plot.binWidth < 12: itemconf(item, fill=color, outline=scaleCol(color, 0.9)) else: itemconf(item, fill=color, outline=scaleCol(color, 0.8)) if title: itemconf(plot.title, text=title) def getDangleResidue(self, dRes, direction): # return a DangleResidue object located offset-residue away from dRes in sequence # Use below function to guard against non-sequentially numbered residues # the below function follows bonds, but uses a cache for speed residue = getLinkedResidue(dRes.residue, direction) if residue and self.dangleChain: return self.dangleChain.findFirstDangleResidue(residue=residue) def showPrevious(self): if not self.dangleResidue: return prevDangleResidue = self.getDangleResidue(self.dangleResidue, 'prev') if not prevDangleResidue: return self.predictionMatrix.selectObject(prevDangleResidue) #self.dangleResidue = prevDangleResidue #self.loadGLEs(self.dangleResidue, None, None) #self.predictionMatrix.currentObject = self.dangleResidue #self.predictionMatrix.hilightObject(self.predictionMatrix.currentObject) def showNext(self): if not self.dangleResidue: return nextDangleResidue = self.getDangleResidue(self.dangleResidue, 'next') if not nextDangleResidue: return self.predictionMatrix.selectObject(nextDangleResidue) #self.dangleResidue = nextDangleResidue #self.loadGLEs(self.dangleResidue, None, None) #self.predictionMatrix.currentObject = self.dangleResidue #self.predictionMatrix.hilightObject(self.predictionMatrix.currentObject) def updatePhiPsi(self, residue): if self.ensemble: phiPsiAccept = [] plotObjects = [] colors = [] cChain = self.ensemble.findFirstCoordChain(code=residue.chain.code) if cChain: cResidue = cChain.findFirstResidue(residue=residue) if cResidue: for model in self.ensemble.models: phiPsiAccept.append(self.getPhiPsi(cResidue, model)) plotObjects.append((cResidue, model)) colors.append(ENSEMBLE_COLOR) if self.colorScheme == 'rainbow': # default grey circles self.plot.updateObjects(phiPsiAccList=phiPsiAccept, objectList=plotObjects) else: # bright green circles self.plot.updateObjects(phiPsiAccList=phiPsiAccept, objectList=plotObjects, colors=colors) def getPhiPsi(self, residue, model=None): phi, psi = getResiduePhiPsi(residue, model=model) return (phi, psi, 1) def getPlotColor(self, i, maxInt=255): mode = self.colorScheme if mode == 'rainbow': if (i == 0): return '#%02x%02x%02x' % (255, 255, 255) # white bg elif (i > 0.75): red = 1 green = (1 - i) / 0.25 blue = 0 elif (i > 0.5): red = (i - 0.5) / 0.25 green = 1 blue = 0 elif (i > 0.25): red = 0 green = 1 blue = (0.5 - i) / 0.25 else: red = 0 green = i / 0.25 blue = 1 return '#%02x%02x%02x' % (red * maxInt, green * maxInt, blue * maxInt) """ elif mode == 'black': if i > 0.5: red = i green = 1 - i blue = 1 - i else: v = 0.1 + (0.9 * i) red = v green = v blue = v elif mode == 'white': if i > 0.5: red = i green = 1 - i blue = 1 - i else: v = 1.0 - (0.9 * i) red = v green = v blue = v return '#%02x%02x%02x' % (red*maxInt, green*maxInt, blue*maxInt) """ # default : red to black if (i == 0): return '#%02x%02x%02x' % (255, 255, 255) # white bg return '#%02x%02x%02x' % (((1 - i) * 255), 0, 0) def updatePredictionMatrixAfter(self, index=None, text=None): if self.chain and self.shiftList and self.dangleStore: self.dangleChain = self.dangleStore.findFirstDangleChain( chain=self.chain, shiftList=self.shiftList) else: self.dangleChain = None self.after_idle(self.updatePredictionMatrix) #if showYesNo('Not Found','No data for Chain %s in Dangle Run %s.\nMake prediction for this chain?' % (self.chain.code, text), parent=self): # self.runDangle() def updatePredictionMatrix(self): shiftList = self.shiftList objectList = [] textMatrix = [] colorMatrix = [] if self.dangleChain: residueList = [(dr.residue.seqCode, dr.residue, dr) for dr in self.dangleChain.dangleResidues] residueList.sort() else: # Chow blank table residueList = [] for seqCode, residue, dRes in residueList: objectList.append(dRes) phi = dRes.phiValue psi = dRes.psiValue ss = dRes.secStrucCode atomNames = [] for atomName in BACKBONE_ATOMS: atom = residue.findFirstAtom(name=atomName) if not atom: continue atomSet = atom.atomSet if atomSet: shifts = getAtomSetShifts(atomSet, shiftList=shiftList) if shifts: atomNames.append(atomName) atomNames.sort() atomNames = ' '.join(atomNames) textMatrix.append((seqCode, residue.ccpCode, dRes.numIslands, ss, phi, psi, dRes.phiUpper, dRes.phiLower, dRes.psiUpper, dRes.psiLower, atomNames)) if (phi is None) and (psi is None): colorMatrix.append(INACTIVE_COLORS) elif dRes.numIslands >= 5: colorMatrix.append(BAD_COLORS) else: colorMatrix.append(GOOD_COLORS) self.predictionMatrix.update(textMatrix=textMatrix, objectList=objectList, colorMatrix=colorMatrix) def selectCell(self, dRes, row, col): self.dangleResidue = dRes self.row = row self.col = col self.loadGLEs(dRes, row, col) def setFloatEntry(self, event): index = self.col - 4 # index of attribute to set in the EDIT_ATTRS list value = self.floatEntry.get() if value is not None: setattr(self.dangleResidue, EDIT_ATTRS[index], value) self.updatePredictionMatrixAfter() def getFloatEntry(self, dangleResidue): if dangleResidue: index = self.col - 4 # index of attribute to set in the EDIT_ATTRS list self.floatEntry.set(getattr(dangleResidue, EDIT_ATTRS[index])) def changeChain(self, chain): if chain is not self.chain: self.chain = chain self.updateEnsemblePulldown( ) # Ensembles are filtered by chains molSystem self.updatePredictionMatrixAfter() def changeShiftList(self, shiftList): if shiftList is not self.shiftList: self.shiftList = shiftList self.updatePredictionMatrixAfter() def changeEnsemble(self, ensemble): self.ensemble = ensemble def changeConstraintSet(self, constraintSet): if constraintSet is not self.constraintSet: self.constraintSet = constraintSet def changeDangleStore(self, dangleStore): if self.dangleStore is not dangleStore: self.dangleStore = dangleStore if dangleStore: self.dangleChain = dangleStore.findFirstDangleChain() self.chain = self.dangleChain.chain self.shiftList = self.dangleChain.shiftList else: self.dangleChain = None self.updateChainPulldown() self.updateShiftListPulldown() self.updateEnsemblePulldown( ) # Ensembles are filtered by chains molSystem self.updatePredictionMatrixAfter() def checkDangleStore(self): if not self.dangleStore: N = len(self.project.dangleStores) + 1 name = askString('Request', 'Dangle Run Name:', 'Run%d' % N, parent=self) if not name: return None for character in whitespace: if character in name: showWarning('Failure', 'Name cannot contain whitespace', parent=self) return None if self.project.findFirstDangleStore(name=name): showWarning('Failure', 'Name already used', parent=self) return None self.dangleStore = self.project.newDangleStore(name=name) self.updateDangleStorePulldown() def updateChainPulldown(self, obj=None): index = 0 names = [] chains = [] chain = self.chain for molSystem in self.project.molSystems: msCode = molSystem.code for chainA in molSystem.chains: residues = chainA.residues if not residues: continue for residue in residues: # Must have at least one protein residue if residue.molType == 'protein': names.append('%s:%s' % (msCode, chainA.code)) chains.append(chainA) break if chains: if chain not in chains: chain = chains[0] index = chains.index(chain) else: chain = None if chain is not self.chain: self.chain = chain self.updatePredictionMatrixAfter() self.chainPulldown.setup(names, chains, index) def updateShiftListPulldown(self, obj=None): index = 0 names = [] shiftLists = getShiftLists(self.nmrProject) if shiftLists: if self.shiftList not in shiftLists: self.shiftList = shiftLists[0] index = shiftLists.index(self.shiftList) names = ['%s:%d' % (sl.name, sl.serial) for sl in shiftLists] else: self.shiftList = None self.shiftListPulldown.setup(names, shiftLists, index) def updateDangleStorePulldown(self): names = [ '<New>', ] dangleStores = [ None, ] for dangleStore in self.project.sortedDangleStores(): names.append(dangleStore.name) dangleStores.append(dangleStore) if self.dangleStore not in dangleStores: self.dangleStore = dangleStores[-1] index = dangleStores.index(self.dangleStore) self.dangleStorePulldown.setup(names, dangleStores, index) def updateEnsemblePulldown(self, obj=None): index = 0 names = [ '<None>', ] ensembles = [ None, ] if self.chain: molSystem = self.chain.molSystem for ensemble in molSystem.sortedStructureEnsembles(): names.append('%s:%d' % (molSystem.code, ensemble.ensembleId)) ensembles.append(ensemble) if self.ensemble not in ensembles: self.ensemble = ensembles[0] index = ensembles.index(self.ensemble) self.ensemblePulldown.setup(names, ensembles, index) def updateConstrSetPulldown(self, obj=None): names = [ '<New>', ] constraintSets = [ None, ] # Use below later, once API speed/loading is improved # for constraintSet in self.nmrProject.sortedNmrConstraintStores(): for constraintSet in self.project.sortedNmrConstraintStores(): names.append('%d' % constraintSet.serial) constraintSets.append(constraintSet) if self.constraintSet not in constraintSets: self.constraintSet = constraintSets[0] index = constraintSets.index(self.constraintSet) self.constrSetPulldown.setup(names, constraintSets, index) def try1(self): if not self.project: return ccpCodes = [ 'Ala', 'Cys', 'Asp', 'Glu', 'Phe', 'Gly', 'His', 'Ile', 'Lys', 'Leu', 'Met', 'Asn', 'Gln', 'Arg', 'Ser', 'Thr', 'Val', 'Trp', 'Tyr', 'Pro' ] atomNames = ['HA', 'CA', 'CB', 'C', 'N'] molType = 'protein' for ccpCode in ccpCodes: for atomName in atomNames: chemAtomNmrRef = getChemAtomNmrRef(self.project, atomName, ccpCode, molType) mean = chemAtomNmrRef.meanValue sd = chemAtomNmrRef.stdDev print '%5s%5s %.3f %.3f' % (ccpCode, atomName, mean, sd)
class EditPeakFindParamsPopup(BasePopup): """ ** Peak Settings and Non-Interactive Peak Finding ** The purpose of this dialog is to allow the user to select settings for finding and integrating peaks, and also to be able to find peaks in an arbitrary region that is specified in a table rather than via a spectrum window. ** Find Parameters tab ** This can be used to specify how peak finding works. First of all, you can search for just positive peaks, just negative peaks or both, and the default is that it is just positive peaks. However, this is further filtered by what the contour levels are. If there are no positive contour levels for a given spectrum then positive peaks are not found even if this dialog says they can be, and similarly if there are no negative contour levels for a given spectrum then negative peaks are not found even if this dialog says they can be. The peak finding algorithm looks for local extrema (maximum for positive peaks and minima for negative peaks). But on a grid there are various ways to define what you mean by an extremum. Suppose you are trying to determine if point p is a maximum (similar considerations apply for minimum). You would want the intensity at all nearby points to be less than or equal to the intensity at p. You can just check points that are just +- one point from p in each dimension, or you can also check "diagonal" points. For example, if you are looking at point p = (x, y) in 2D, then the former would mean checking the four points (x-1, y), (x+1, y) (x, y-1) and (x, y+1), whereas for the latter you would also have to check (x-1, y-1), (x-1, y+1), (x+1, y-1) and (x+1, y+1). In N dimensions the "diagonal" method involves checking 3^N-1 points whereas the "non-diagonal" method involves checking only 2N points. In general the "non-diagonal" method is probably the one to use, and it is the default. Peaks are only found above (for positive peaks) or below (for negative peaks) some threshold. By default this is determined by the contour level for the spectrum. For positive peaks the threshold is the minimum positive contour level, and for negative peaks the threshold is the maximum negative contour level. However these levels can be scaled up (or down) using the "Scale relative to contour levels" option (default value 1). For example, if you have drawn the contour levels low to show a bit of noise, but do not want the noise picked as peaks, then you could select a scale of 2 (or whatever) to increase the threshold. The "Exclusion buffer around peaks" is so that in crowded regions you do not get too many peaks near one location. By default the exclusion buffer is 1 point in each dimension, but this can be increased to make the algorithm find fewer peaks. By default the peak finding only looks at the orthogonal region that is displayed in the given window where peak finding is taking place. Sometimes it looks like a peak should be found because in x, y you can see an extremum, but unless it is also an extremum in the orthogonal dimensions it is not picked. You can widen out the points being examined in the orthogonal dimensions by using the "Extra thickness in orthogonal dims" option, which is specified in points. The "Minimum drop factor" is by what factor the intensity needs to drop from its extreme value for there to be considered to be a peak. This could help remove sinc wiggle peaks, for example. The default is that the drop factor is 0, which in effect means that there is no condition. The "Volume method" is what is used to estimate the volume of peaks that are found. The default is "box sum", which just looks at a fixed size box around the peak centre and sums the intensities in that. The size of the box is set in the table in the Spectrum Widths tab. The "truncated box sum" is the same as "box sum" except that the summing stops in a given direction when (if) the intensities start increasing. The "parabolic" fit fits a quadratic equation in each dimension to the intensity at the peak centre and ad +- 1 points and then uses the equivalent Gaussian fit to estimate the volume. ** Spectrum Widths ** This can be used to specify minimum linewidths (in Hz) for there to be considered a peak to exist in the peak finding algorithm. It is also where the Boxwidth for each dimension in each spectrum is specified. ** Diagonal Exclusions ** This can be used to exclude peaks from being found in regions near the diagonal (so in homonuclear experiments). The exclusion region is specified in ppm and is independent of spectrum. ** Region Peak Find ** This can be used to find peaks non-interactively (so not having to control shift drag inside a spectrum window). The region being analysed is specified in the table. There are two types of conditions that can be specified, "include" for regions that should be included and "exclude" for regions that should be excluded. The regions are specified in ppm. The "Whole Region" button will set the selected row in the table to be the entire fundamental region of the spectrum. The "Add Region" button adds an extra row to the table, and the "Delete Region" button removes the selected row. The "Adjust Params" button goes to the Find Parameters tab. The "Find Peaks!" button does the peak finding. """ def __init__(self, parent, *args, **kw): self.spectrum = None BasePopup.__init__(self, parent=parent, title='Peak : Peak Finding', **kw) def body(self, guiFrame): self.geometry('600x350') guiFrame.expandGrid(0, 0) tipTexts = ['', '', '', ''] options = [ 'Find Parameters', 'Spectrum Widths', 'Diagonal Exclusions', 'Region Peak Find' ] tabbedFrame = TabbedFrame(guiFrame, options=options, grid=(0, 0)) frameA, frameB, frameC, frameD = tabbedFrame.frames self.tabbedFrame = tabbedFrame # Find Params frameA.expandGrid(2, 0) row = 0 label = LabelFrame(frameA, text='Extrema to search for:', grid=(row, 0), gridSpan=(1, 2)) label.expandGrid(0, 1) entries = ['positive and negative', 'positive only', 'negative only'] tipTexts = [ 'Sets whether peak picking within spectra find intensity maxima, minima or both maxima and minima', ] self.extrema_buttons = RadioButtons(label, entries=entries, select_callback=self.apply, direction='horizontal', grid=(0, 0), tipTexts=tipTexts) row += 1 label = LabelFrame(frameA, text='Nearby points to check:', grid=(row, 0), gridSpan=(1, 2)) label.expandGrid(None, 1) entries = ['+-1 in at most one dim', '+-1 allowed in any dim'] tipTexts = [ 'Sets how permissive the peak picking in when searching for intensity extrema; by adding extra points to the selected search region', ] self.adjacent_buttons = RadioButtons(label, entries=entries, select_callback=self.apply, direction='horizontal', grid=(0, 0), tipTexts=tipTexts) row += 1 labelFrame = LabelFrame(frameA, text='Other parameters:', grid=(row, 0), gridSpan=(1, 2)) labelFrame.expandGrid(5, 2) frow = 0 label = Label(labelFrame, text='Scale relative to contour levels:', grid=(frow, 0), sticky='e') tipText = 'Threshold above which peaks are picked, relative to the lowest displayed contour; 1.0 means picking exactly what is visible' self.scale_entry = FloatEntry(labelFrame, grid=(frow, 1), tipText=tipText, returnCallback=self.apply, width=10) self.scale_entry.bind('<Leave>', self.apply, '+') frow += 1 label = Label(labelFrame, text='Exclusion buffer around peaks (in points):', grid=(frow, 0), sticky='e') tipText = 'The size of the no-pick region, in data points, around existing picked peaks; eliminates duplicate picking' self.buffer_entry = IntEntry(labelFrame, returnCallback=self.apply, grid=(frow, 1), width=10, tipText=tipText) self.buffer_entry.bind('<Leave>', self.apply, '+') frow += 1 label = Label(labelFrame, text='Extra thickness in orthogonal dims (in points):', grid=(frow, 0), sticky='e') tipText = 'Sets whether to consider any additional planes (Z dimension) when calculating peak volume integrals' self.thickness_entry = IntEntry(labelFrame, returnCallback=self.apply, width=10, grid=(frow, 1), tipText=tipText) self.thickness_entry.bind('<Leave>', self.apply, '+') frow += 1 label = Label(labelFrame, text='Minimum drop factor (0.0-1.0):', grid=(frow, 0), sticky='e') tipText = '' self.drop_entry = FloatEntry(labelFrame, returnCallback=self.apply, width=10, grid=(frow, 1), tipText=tipText) self.drop_entry.bind('<Leave>', self.apply, '+') frow += 1 label = Label(labelFrame, text='Volume method:', grid=(frow, 0), sticky='e') tipText = 'Selects which method to use to calculate peak volume integrals when peaks are picked; box sizes are specified in "Spectrum Widths"' self.method_menu = PulldownList(labelFrame, texts=PeakBasic.PEAK_VOLUME_METHODS, grid=(frow, 1), callback=self.apply, tipText=tipText) # Spectrum widths frameB.expandGrid(1, 1) label = Label(frameB, text='Spectrum: ') label.grid(row=0, column=0, sticky='e') tipText = 'The spectrum which determines the widths being shown' self.expt_spectrum = PulldownList(frameB, tipText=tipText, callback=self.setSpectrumProperties) self.expt_spectrum.grid(row=0, column=1, sticky='w') self.editLinewidthEntry = FloatEntry(self, text='', returnCallback=self.setLinewidth, width=10) self.editBoxwidthEntry = FloatEntry(self, text='', returnCallback=self.setBoxwidth, width=10) tipTexts = [ 'The number of the spectrum dimension to which the settings apply', 'The nuclear isotope measures in the spectrum dimension', 'The smallest value for the linewidth of a peak for it to be picked', 'The size of the spectrum region to perform the volume integral over' ] headingList = [ 'Dimension', 'Isotope', 'Minimum Linewidth (Hz)', 'Boxwidth' ] editSetCallbacks = [None, None, self.setLinewidth, self.setBoxwidth] editGetCallbacks = [None, None, self.getLinewidth, self.getBoxwidth] editWidgets = [ None, None, self.editLinewidthEntry, self.editBoxwidthEntry ] self.spectrumMatrix = ScrolledMatrix(frameB, initialRows=6, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, headingList=headingList, callback=self.selectCell, tipTexts=tipTexts) self.spectrumMatrix.grid(row=1, column=0, columnspan=2, sticky='nsew') # Diagonal Exclusions frameC.expandGrid(0, 0) tipTexts = [ 'The isotope as measures on the axis of a spectrum window', 'The distance from the homonuclear diagonal line within which no peak picking can occur' ] self.exclusionEntry = FloatEntry(self, text='', returnCallback=self.setExclusion, width=10) headingList = ['Isotope', 'Diagonal Exclusion (ppm)'] editSetCallbacks = [None, self.setExclusion] editGetCallbacks = [None, self.getExclusion] editWidgets = [None, self.exclusionEntry] self.isotopeMatrix = ScrolledMatrix(frameC, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, headingList=headingList, grid=(0, 0), tipTexts=tipTexts) # Region peak find self.regionFindPeakList = None self.regionCondition = None self.regionConditions = [] self.regionCol = 1 row = 0 label = Label(frameD, text='Peak List: ', grid=(0, 0)) tipText = 'Selects which peak list to perform region-wide peak picking for' self.regionPeakListPulldown = PulldownList( frameD, callback=self.changeRegionPeakList, grid=(0, 1), tipText=tipText) row += 1 frameD.expandGrid(row, 1) self.regionEntry = FloatEntry(self, text='', returnCallback=self.setRegion, width=10) self.conditionMenu = PulldownList(self, texts=('include', 'exclude'), callback=self.setCondition) tipTexts = [ 'Whether to include or exclude the states region from region-wide peak picking', ] headingList = ['Condition'] editSetCallbacks = [None] editGetCallbacks = [None] editWidgets = [self.conditionMenu] self.regionFindMatrix = ScrolledMatrix( frameD, headingList=headingList, callback=self.selectRegionCell, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, grid=(row, 0), gridSpan=(1, 2)) row += 1 tipTexts = [ 'Sets the currently selected region row to cover the whole spectrum', 'Add a new region row, which may them be set for exclusion or inclusion when peak picking large areas', 'Remove the selected region specification', 'Go to the panel for setting the parameters that control how peaks extrema are picked', 'Using the stated regions and parameters, perform region-wide peak picking' ] texts = [ 'Whole Region', 'Add Region', 'Delete Region', 'Adjust Params', 'Find Peaks!' ] commands = [ self.wholeRegion, self.addCondition, self.deleteCondition, self.adjustParams, self.regionFindPeaks ] buttons = ButtonList(frameD, texts=texts, commands=commands, grid=(row, 0), gridSpan=(1, 2), tipTexts=tipTexts) buttons.buttons[4].config(bg='#B0FFB0') utilButtons = UtilityButtonList(tabbedFrame.sideFrame, grid=(0, 0), helpUrl=self.help_url, sticky='e') self.dataDim = None self.setParamsEntries() self.updateSpectrum() self.setIsotopeProperties() self.updateRegionPeakLists() self.administerNotifiers(self.registerNotify) def administerNotifiers(self, notifyFunc): # Many more needed here, esp on the AnalysisProject prams for func in ('__init__', 'delete', 'setName'): notifyFunc(self.updateRegionPeakLists, 'ccp.nmr.Nmr.DataSource', func) notifyFunc(self.updateRegionPeakLists, 'ccp.nmr.Nmr.Experiment', func) for func in ('__init__', 'delete'): notifyFunc(self.updateRegionPeakLists, 'ccp.nmr.Nmr.PeakList', func) for clazz in ('Experiment', 'DataSource'): for func in ('__init__', 'delete', 'setName'): notifyFunc(self.updateSpectrumTable, 'ccp.nmr.Nmr.%s' % clazz, func) for func in ('setPeakFindBoxWidth', 'setPeakFindMinLineWidth'): notifyFunc(self.updateSpectrumTable, 'ccpnmr.Analysis.AnalysisDataDim', func) def destroy(self): self.administerNotifiers(self.unregisterNotify) BasePopup.destroy(self) def updateSpectrum(self, spectrum=None): if not spectrum: spectrum = self.spectrum spectra = self.parent.getSpectra() if spectra: if spectrum not in spectra: spectrum = spectra[0] index = spectra.index(spectrum) names = ['%s:%s' % (x.experiment.name, x.name) for x in spectra] else: index = 0 names = [] self.expt_spectrum.setup(names, spectra, index) self.setSpectrumProperties(spectrum) def updateNotifier(self, *extra): self.updateSpectrum() def setLinewidth(self, *event): value = self.editLinewidthEntry.get() if value is not None: PeakFindParams.setPeakFindMinLinewidth(self.dataDim, value) self.setSpectrumProperties(self.dataDim.dataSource) def getLinewidth(self, dataDim): if dataDim: self.editLinewidthEntry.set( PeakFindParams.getPeakFindMinLinewidth(self.dataDim)) def setBoxwidth(self, *event): value = self.editBoxwidthEntry.get() if value is not None: PeakFindParams.setPeakFindBoxwidth(self.dataDim, value) self.setSpectrumProperties(self.dataDim.dataSource) def getBoxwidth(self, dataDim): if dataDim: self.editBoxwidthEntry.set( PeakFindParams.getPeakFindBoxwidth(self.dataDim)) def selectCell(self, object, row, col): self.dataDim = object def setExclusion(self, *extra): isotope = self.isotopeMatrix.currentObject if not isotope: return value = self.exclusionEntry.get() if value is not None: setIsotopeExclusion(isotope, value) self.setIsotopeProperties() def getExclusion(self, isotope): value = getIsotopeExclusion(isotope) self.exclusionEntry.set(value) def setParamsEntries(self): project = self.project params = PeakFindParams.getPeakFindParams(project) self.scale_entry.set(params['scale']) self.buffer_entry.set(params['buffer']) self.thickness_entry.set(params['thickness']) self.drop_entry.set(params['drop']) volumeMethod = params['volumeMethod'] if volumeMethod == 'parabolic fit': volumeMethod = PeakBasic.PEAK_VOLUME_METHODS[0] self.method_menu.set(params['volumeMethod']) if (params['nonadjacent']): n = 1 else: n = 0 self.adjacent_buttons.setIndex(n) have_high = params['haveHigh'] have_low = params['haveLow'] if (have_high and have_low): n = 0 elif (have_high): n = 1 else: n = 2 self.extrema_buttons.setIndex(n) def apply(self, *extra): params = {} params['scale'] = self.scale_entry.get() params['buffer'] = self.buffer_entry.get() params['thickness'] = self.thickness_entry.get() params['drop'] = self.drop_entry.get() params['volumeMethod'] = self.method_menu.getText() n = self.adjacent_buttons.getIndex() if (n == 0): nonadjacent = False else: nonadjacent = True params['nonadjacent'] = nonadjacent n = self.extrema_buttons.getIndex() if (n == 0): have_high = True have_low = True elif (n == 1): have_high = True have_low = False elif (n == 2): have_high = False have_low = True params['haveHigh'] = have_high params['haveLow'] = have_low project = self.project try: PeakFindParams.setPeakFindParams(project, params) except Implementation.ApiError, e: showError('Parameter error', e.error_msg, parent=self)
class 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