class EditFitGraphPopup(BasePopup): """ **Analyse Function Curve Fitting to Peak Series Data** This popup is used to display the fit of a curve, of the displayed equation type, to data that has been extracted for a group of spectrum peaks from an NMR series. Precisely which kind of data is being fitted depends on the parent tool that this popup window was launched from. For example, for the `Follow Intensity Changes`_ tool the displayed graph is of a time or frequency value on the "X" axis (e.g. T1) verses peak intensity. For the `Follow Shift Changes`_ system the plot is for the parameters of the experiment titration on the "X" axis (e.g concentration) verses chemical shift distance. The upper graph shows the peak data that was used; the blue points, and the points of the fitted theoretical curve; red points. The lower table lists the data points for all of the peaks in the group, to which the data relates. There will be one peak for each row of the table, and hence point on the "X" axis. For each data point in the table the corresponding peak from which the data was extracted may be located with the "Follow in window" option, using the stated spectrum window and making marker lines as desired. Alternatively, the peak may be viewed in a table with [Show Peak] In general operation this system is used to check how well the selected function curve fits the peak data. The data that is displayed comes from the analysis tool that launched this popup. Any outliers may be removed using [Remove Point] (only with good reason) or the peaks themselves may be corrected if something has gone awry. Given that the display is launched from the selection of a specific group of peaks from a popup like `Follow Shift Changes`_ or `Follow Intensity Changes`_ and there are usually several peak groups that need to be analysed, the [Previous Set] and [Next Set] buttons can be used to quickly jump to the next peak group and see the next curve in the analysis results. .. _`Follow Intensity Changes`: CalcRatesPopup.html .. _`Follow Shift Changes`: FollowShiftChangesPopup.html """ def __init__(self, parent, dataFitting, getXYfunction, updateFunction, xLabel, yLabel, showObjectFunction=None, nextSetFunction=None, prevSetFunction=None, graphTitle='', **kw): self.guiParent = parent self.getXYfunction = getXYfunction self.updateFunction = updateFunction self.nextSetFunction = nextSetFunction self.prevSetFunction = prevSetFunction self.dataFitting = dataFitting self.object = None self.xLabel = xLabel self.yLabel = yLabel self.x = [] self.y = [] self.yFit = [] self.params = () self.chiSq = 0 self.method = dataFitting.fitFunction self.waiting = False self.noiseLevel = dataFitting.noiseLevel self.graphTitle = graphTitle self.windowPane = None self.mark = None BasePopup.__init__(self, parent=parent, title='Fit Graph', **kw) parent.protocol("WM_DELETE_WINDOW", self.close) def body(self, guiFrame): guiFrame.grid_columnconfigure(0, weight=1) row = 0 self.scrolledGraph = ScrolledGraph(guiFrame, width=400, height=300, symbolSize=5, symbols=['square', 'circle'], dataColors=['#000080', '#800000'], lineWidths=[0, 1], grid=(row, 0)) #self.scrolledGraph.setZoom(0.7) row += 1 frame = Frame(guiFrame, grid=(row, 0), sticky='ew') label = Label(frame, text='Fitting Function:', grid=(0, 0)) tipText = 'Selects which form of function to fit to the experimental data' self.methodPulldown = PulldownList(frame, self.changeMethod, grid=(0, 1), tipText=tipText) row += 1 frame = Frame(guiFrame, grid=(row, 0), sticky='ew') tipText = 'The effective equation of the final fitted graph, incorporating all parameters' self.equationLabel = Label(frame, text='Equation:', grid=(0, 0), tipText=tipText) tipText = 'The error in the fit of the selected parameterised function to the experimental data' self.errorLabel = Label(frame, text='Fit Error:', grid=(0, 1), tipText=tipText) row += 1 frame = Frame(guiFrame, grid=(row, 0), sticky='ew') label = Label(frame, text='Include x origin?:', grid=(0, 0)) tipText = 'Whether to include the x=0 point in the drawing' self.xOriginSelect = CheckButton(frame, callback=self.draw, grid=(0, 1), selected=False, tipText=tipText) label = Label(frame, text='Include y origin?:', grid=(0, 2)) tipText = 'Whether to include the y=0 point in the drawing' self.yOriginSelect = CheckButton(frame, callback=self.draw, grid=(0, 3), selected=False, tipText=tipText) label = Label(frame, text='Include y error?:', grid=(0, 4)) tipText = 'Whether to include the y error bars in the drawing (if these exist)' self.yErrorSelect = CheckButton(frame, callback=self.draw, grid=(0, 5), selected=False, tipText=tipText) row += 1 frame = Frame(guiFrame, grid=(row, 0), sticky='ew') label = Label(frame, text='Navigation Window:', grid=(0, 0)) tipText = 'Selects which spectrum window will be used for navigating to peak positions' self.windowPanePulldown = PulldownList(frame, self.changeWindow, grid=(0, 1), tipText=tipText) label = Label(frame, text='Follow in window?:', grid=(0, 2)) tipText = 'Whether to navigate to the position of the reference peak (for the group), in the selected window' self.followSelect = CheckButton(frame, callback=self.windowPaneNavigate, grid=(0, 3), selected=False, tipText=tipText) label = Label(frame, text='Mark Ref Peak?:', grid=(0, 4)) tipText = 'Whether to put a multi-dimensional cross-mark through the reference peak position, so it can be identified in spectra' self.markSelect = CheckButton(frame, callback=None, tipText=tipText, grid=(0, 5), selected=False) row += 1 guiFrame.grid_rowconfigure(row, weight=1) tipTexts = [ 'The number of the data point, in order of increasing X-axis value', 'For each point, the value of the parameter which is varied in the NMR series, e.g. T1, temperature, concentration etc.', 'For each point, the experimental value being fitted, e.g. peak intensity of chemical shift distance', 'The value of the best-fit function at the X-axis location', 'The difference between the experimental (Y-axis) value and the fitted value', 'The error in the experimental (Y-axis) value' ] headingList = ['Point', 'x', 'y', 'Fitted y', u'\u0394', 'y error'] self.scrolledMatrix = ScrolledMatrix(guiFrame, headingList=headingList, callback=self.selectObject, tipTexts=tipTexts, grid=(row, 0)) row += 1 tipTexts = [ 'Remove the selected data point, optionally removing the underlying peak', 'Show a table of spectrum peaks that correspond to the selected data point' ] texts = ['Remove Point', 'Show Peak'] commands = [self.removePoint, self.showObject] if self.prevSetFunction: texts.append('Previous Set') tipTexts.append( 'Move to the previous set of fitted values; the next group of peaks, often corresponding to a different residue or resonance' ) commands.append(self.prevSet) if self.nextSetFunction: tipTexts.append( 'Move to the next set of fitted values; the next group of peaks, often corresponding to a different residue or resonance' ) texts.append('Next Set') commands.append(self.nextSet) bottomButtons = UtilityButtonList(guiFrame, texts=texts, commands=commands, helpUrl=self.help_url, tipTexts=tipTexts, grid=(row, 0), doClone=False) self.removeButton = bottomButtons.buttons[0] for func in ('__init__', 'delete', 'setName'): self.registerNotify(self.updateWindows, 'ccpnmr.Analysis.SpectrumWindow', func) self.update() def destroy(self): for func in ('__init__', 'delete', 'setName'): self.unregisterNotify(self.updateWindows, 'ccpnmr.Analysis.SpectrumWindow', func) BasePopup.destroy(self) def nextSet(self): if self.nextSetFunction: self.nextSetFunction() self.windowPaneNavigate() def prevSet(self): if self.prevSetFunction: self.prevSetFunction() self.windowPaneNavigate() def windowPaneNavigate(self, event=None): if self.followSelect.get() and self.dataFitting: peaks = self.dataFitting.objects if self.windowPane and peaks: peak = peaks[int(len(peaks) / 2)] windowFrame = self.windowPane.getWindowFrame() windowFrame.gotoPeak(peak) if self.markSelect.get(): if self.mark and not self.mark.isDeleted: self.mark.delete() self.mark = createPeakMark(self.dataFitting.refObject) def getWindows(self): peaks = self.scrolledMatrix.objectList windowPanes = [] if peaks: peak = peaks[0] project = peak.root spectrum = peak.peakList.dataSource tryWindows = getActiveWindows(project) for window in tryWindows: for windowPane in window.sortedSpectrumWindowPanes(): if isSpectrumInWindowPane(windowPane, spectrum): windowPanes.append(windowPane) return windowPanes def changeWindow(self, windowPane): if windowPane is not self.windowPane: self.windowPane = windowPane self.windowPaneNavigate() def updateWindows(self, window=None): windowPanes = self.getWindows() index = -1 names = [getWindowPaneName(wp) for wp in windowPanes] windowPane = self.windowPane if windowPanes: if windowPane not in windowPanes: windowPane = windowPanes[0] index = windowPanes.index(windowPane) if windowPane is not self.windowPane: self.windowPane = windowPane self.windowPaneNavigate() self.windowPanePulldown.setup(names, windowPanes, index) def showObject(self): if self.object and (self.object.className == 'Peak'): peaks = [self.object] self.guiParent.guiParent.viewPeaks(peaks) def close(self): BasePopup.close(self) def changeMethod(self, index): if self.dataFitting: self.method = index self.dataFitting.fitFunction = self.method self.updateAfter() def updateMethods(self, *opt): n = len(METHOD_NAMES) if self.method >= n: self.method = 1 self.methodPulldown.setup(METHOD_NAMES, range(n), self.method) def selectObject(self, object, row, col): if object: self.object = object self.updateButtons() def removePoint(self): if self.object and (len(self.dataFitting.objects) > 2): self.dataFitting.objects.remove(self.object) if showYesNo('Query', 'Delete the corresponding %s?' % (self.object.className), parent=self): self.object.delete() self.updateAfter() def draw(self, *junk): title = '%s Function fit %s' % (self.graphTitle, METHOD_NAMES[self.method]) dataSet1 = [] dataSet2 = [] useErr = self.yErrorSelect.isSelected() if not useErr: useErr = None for i in range(len(self.scrolledMatrix.textMatrix)): row = self.scrolledMatrix.textMatrix[i] x = row[1] y = row[2] y2 = row[3] err = useErr and row[5] dataSet1.append([x, y, err]) dataSet2.append([x, y2]) dataSet1.sort() dataSet2.sort() if dataSet1 and dataSet2: drawOriginX = self.xOriginSelect.isSelected() drawOriginY = self.yOriginSelect.isSelected() self.scrolledGraph.update(dataSets=[dataSet1, dataSet2], xLabel=self.xLabel, yLabel=self.yLabel, title=title, drawOriginX=drawOriginX, drawOriginY=drawOriginY) self.scrolledGraph.draw() def updateAfter(self, *object): if self.waiting: return else: self.waiting = True self.after_idle(self.update) def update(self, dataFitting=None, xLabel=None, yLabel=None, graphTitle=None, force=False): if (not force) and dataFitting and (self.dataFitting is dataFitting): if (self.method, self.xLabel, self.yLabel, self.noiseLevel) == \ (self.dataFitting.fitFunction, xLabel, yLabel, self.dataFitting.noiseLevel): self.waiting = False return if dataFitting: self.dataFitting = dataFitting self.xLabel = xLabel or self.xLabel self.yLabel = yLabel or self.yLabel self.graphTitle = graphTitle or self.graphTitle if not self.dataFitting: self.waiting = False return else: dataFitting = self.dataFitting dataFitting = self.getXYfunction(dataFitting) if dataFitting.fitFunction is not None: self.method = dataFitting.fitFunction self.noiseLevel = dataFitting.noiseLevel or self.noiseLevel self.updateMethods() isFitted = dataFitting.fit() textMatrix = [] objectList = [] if isFitted and self.method: methodInfo = getFitMethodInfo()[self.method - 1] textFormat, indices = methodInfo[2] textParams = [dataFitting.parameters[i] or 0 for i in indices] equationText = textFormat % tuple(textParams) errorText = '%4f' % dataFitting.fitError x = dataFitting.dataX y = dataFitting.dataY yFit = dataFitting.fittedY N = len(x) err = hasattr(dataFitting, 'dataErr') and dataFitting.dataErr if not err: err = None # wb104: 10 Mar 2014: not sure why the below was here, it isn't needed #if self.method == 10: # x = [sqrt(v) for v in x] data = [(x[i], y[i], yFit[i], y[i] - yFit[i], dataFitting.objects[i], err and err[i]) for i in range(N)] data.sort() for i in range(N): xi, yi, yFiti, deltai, object, erri = data[i] textMatrix.append([i + 1, xi, yi, yFiti, deltai, erri]) objectList.append(object) else: equationText = 'Equation: <No Fit>' errorText = '<None>' x = dataFitting.dataX y = dataFitting.dataY for i, x in enumerate(x): textMatrix.append([i + 1, x, y[i], None, None, None]) objectList.append(dataFitting.objects[i]) self.equationLabel.set('Equation: %s' % equationText) self.errorLabel.set('Fit Error: %s' % errorText) self.scrolledMatrix.update(textMatrix=textMatrix, objectList=objectList) self.updateWindows() self.updateButtons() self.draw() if self.updateFunction: self.updateFunction(dataFitting) self.waiting = False def updateButtons(self): if self.object: self.removeButton.enable() else: self.removeButton.disable()
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 CreateShiftListPopup(BasePopup): help_url = joinPath(getHelpUrlDir(), 'CreateShiftList.html') def __init__(self, parent, project): self.project = project self.peakLists = [] BasePopup.__init__(self, parent=parent, title="Project '%s': " % project.name + 'Create shift list from peak lists', modal=False, transient=True) def body(self, master): # # Peaklist setup # self.peakListDict = {} for experiment in self.project.currentNmrProject.sortedExperiments(): for dataSource in experiment.sortedDataSources(): for peakList in dataSource.sortedPeakLists(): peakListLabel = "%s:%s:%d:%s" % ( experiment.name, dataSource.name, peakList.serial, peakList.name) self.peakListDict[peakListLabel] = peakList peakListLabels = self.peakListDict.keys() peakListLabels.sort() # # chemical shift list setup # self.shiftListDict = {} self.shiftListDict['None'] = None for shiftList in self.project.currentNmrProject.findAllMeasurementLists( className='ShiftList'): shiftListLabel = "%d:%s" % (shiftList.serial, shiftList.name) self.shiftListDict[shiftListLabel] = shiftList shiftListLabels = self.shiftListDict.keys() shiftListLabels.sort() row = 0 label = Label(master, text="Generation of chemical shift list") label.grid(row=row, column=0, columnspan=2, sticky=Tkinter.W) row += 1 label = Label(master, text="Peak lists:") label.grid(row=row, column=0, sticky=Tkinter.W) self.peakListBox = ScrolledListbox(master, width=50, height=5, selectmode=Tkinter.MULTIPLE, initial_list=peakListLabels) self.peakListBox.grid(row=row, column=1, sticky=Tkinter.EW) row += 1 label = Label(master, text="Use existing shift list:") label.grid(row=row, column=0, sticky=Tkinter.W) self.shiftListSelect = PulldownMenu(master, entries=shiftListLabels) self.shiftListSelect.grid(row=row, column=1, sticky=Tkinter.EW) row += 1 label = Label(master, text="Use multiple assignments:") label.grid(row=row, column=0, sticky=Tkinter.W) self.useAllContribs = CheckButton(master) self.useAllContribs.grid(row=row, column=1, sticky=Tkinter.W) # # Setup the default shift error per nucleus # self.defaultShiftError = {} for (nucl, text, defValue) in [('1H', 'proton', '0.002'), ('13C', 'carbon', '0.1'), ('15N', 'nitrogen', '0.1')]: row += 1 label = Label(master, text="Default %s shift error:" % text) label.grid(row=row, column=0, sticky=Tkinter.W) self.defaultShiftError[nucl] = Entry(master, text=defValue) self.defaultShiftError[nucl].grid(row=row, column=1, sticky=Tkinter.W) row += 1 texts = ['Create shift list'] commands = [ self.ok ] # This calls 'ok' in BasePopup, this then calls 'apply' in here buttons = createDismissHelpButtonList(master, texts=texts, commands=commands, help_url=self.help_url) buttons.grid(row=row, columnspan=2, column=0) def apply(self): selectedPeakLists = self.peakListBox.getSelectedItems() for peakListLabel in selectedPeakLists: self.peakLists.append(self.peakListDict[peakListLabel]) chemShiftList = self.shiftListDict[self.shiftListSelect.getSelected()] if not chemShiftList: chemShiftListName = askString("Enter chemical shift list name", "New chemical shift list name", '', self) else: chemShiftListName = chemShiftList.name useAllContribs = self.useAllContribs.isSelected() defaultShiftError = {} for nucl in self.defaultShiftError.keys(): defaultShiftError[nucl] = returnFloat( self.defaultShiftError[nucl].get()) listCreated = createChemShifts(self.peakLists, guiParent=self.parent, multiDialog=self.parent.multiDialog, shiftList=chemShiftList, useAllContribs=useAllContribs, defaultShiftError=defaultShiftError, shiftListName=chemShiftListName) if listCreated: showInfo( "Success", "Succesfully created a chemical shift list from peaklist(s)") return True
class ArchiveProjectPopup(BasePopup): """ **Archive the Project** This popup window enables the user to archive the CCPN Project into a gzipped tar file. By default it only includes the *.xml files which reside in the project directory. If the "Include *.xml.bak files" check button is checked then it will also include the *.xml.bak files which reside in the project directory. If the "Include data files which are in the project directory" check button is checked then it will also include the binary data files which reside in the project directory. **Caveats & Tips** The archive excludes the reference data *.xml files (quite sensibly). """ def __init__(self, parent, project, title='Project : Archive', callback=None, help_msg='', help_url='', dismiss_text='', *args, **kw): self.callback = callback self.help_msg = help_msg self.help_url = help_url self.dismiss_text = dismiss_text BasePopup.__init__(self, parent=parent, project=project, title=title, *args, **kw) def body(self, guiParent): now = datetime.date.today().strftime('%y%m%d') filePrefix = '%s_%s' % (self.project.name, now) projDir = getUserDataPath(self.project) directory = os.path.dirname(projDir) guiParent.grid_rowconfigure(0, weight=1) guiParent.grid_columnconfigure(1, weight=1) row = 0 label = Label(guiParent, text='Archive File:') label.grid(row=row, column=0, sticky=Tkinter.E) tipText = 'File name (excluding .tgz ending) for archive' self.fileEntry = Entry(guiParent, text=filePrefix, tipText=tipText) self.fileEntry.grid(row=row, column=1, sticky=Tkinter.EW) label = Label(guiParent, text='.tgz (automatically appended)') label.grid(row=row, column=2, sticky=Tkinter.W) row = row + 1 self.backupCheck = CheckButton( guiParent, text='Include *.xml.bak files', tipText='If checked include *.xml.bak files') self.backupCheck.grid(row=row, column=1, columnspan=2, sticky=Tkinter.W) row = row + 1 self.dataCheck = CheckButton( guiParent, text='Include data files which are in project directory', tipText= 'If checked include data files if they are located in project directory' ) self.dataCheck.grid(row=row, column=1, columnspan=2, sticky=Tkinter.W) row = row + 1 labelFrame = LabelFrame(guiParent, text='Archive Location') labelFrame.grid(row=row, column=0, columnspan=3, sticky=Tkinter.NSEW) labelFrame.grid_rowconfigure(0, weight=1) labelFrame.grid_columnconfigure(0, weight=1) self.dirSelect = FileSelect(labelFrame, directory=directory, show_file=False) self.dirSelect.grid(row=0, column=0, sticky=Tkinter.NSEW) guiParent.grid_rowconfigure(row, weight=1) row = row + 1 texts = ['Save'] tipTexts = ['Create archive file'] commands = [self.save] buttons = createDismissHelpButtonList(guiParent, texts=texts, tipTexts=tipTexts, commands=commands, help_msg=self.help_msg, help_url=self.help_url, dismiss_text=self.dismiss_text, expands=True) buttons.grid(row=row, column=0, columnspan=3, sticky=Tkinter.EW) def save(self): filePrefix = self.fileEntry.get() directory = self.dirSelect.getDirectory() filePrefix = joinPath(directory, filePrefix) fileName = filePrefix + '.tgz' if os.path.exists(fileName): if not showYesNo('File exists', 'File "%s" exists, overwrite?' % fileName, parent=self): return includeBackups = self.backupCheck.isSelected() includeData = self.dataCheck.isSelected() packageProject(self.project, filePrefix, includeBackups, includeData)
class SaveProjectFrame(Frame): def __init__(self, guiParent, project, callback=None, help_msg='', help_url='', dismiss_text='', modal=False, *args, **kw): self.project = project self.callback = callback self.help_msg = help_msg self.help_url = help_url self.dismiss_text = dismiss_text self.modal = modal self.did_save = False Frame.__init__(self, guiParent, *args, **kw) projDir = Io.getUserDataPath(self.project) guiParent.grid_columnconfigure(1, weight=1) row = 0 label = Label(guiParent, text='Project Name:') label.grid(row=row, column=0, sticky=Tkinter.E) self.proj_name_entry = Entry( guiParent, text=self.project.name, returnCallback=self.updateInfo, leaveCallback=self.updateInfo, tipText='The name used for the project save directory') self.proj_name_entry.grid(row=row, column=1, sticky=Tkinter.EW) row = row + 1 label = Label(guiParent, text='Project Directory:') label.grid(row=row, column=0, sticky=Tkinter.E) label = self.proj_dir_label = Label(guiParent, text=projDir) label.grid(row=row, column=1, sticky=Tkinter.W) row = row + 1 label = Label( guiParent, text='Note: Project Directory = Save Location + Project Name') label.grid(row=row, column=1, sticky=Tkinter.W) text = 'Save binary data with project' tipText = 'Copy data files (e.g. for spectra) into new project directory if not already in current project directory: careful, this can take some time' row = row + 1 self.dataCheckButton = CheckButton(guiParent, text=text, tipText=tipText) self.dataCheckButton.grid(row=row, column=0, columnspan=2, sticky=Tkinter.W) row = row + 1 guiParent.grid_rowconfigure(row, weight=1) labelFrame = LabelFrame(guiParent, text='Save Location') labelFrame.grid(row=row, column=0, columnspan=2, sticky=Tkinter.NSEW) labelFrame.grid_rowconfigure(0, weight=1) labelFrame.grid_columnconfigure(0, weight=1) directory = os.path.dirname(projDir) self.proj_dir_select = FileSelect( labelFrame, directory=directory, select_dir_callback=self.selectDir, change_dir_callback=self.updateInfo, should_change_dir_callback=self.shouldChangeDir, getRowColor=self.getEntryColor, show_file=False) self.proj_dir_select.grid(row=0, column=0, sticky=Tkinter.NSEW) row = row + 1 texts = ['Save'] tipTexts = ['Save project with specified name in specified directory'] commands = [self.save] buttons = createDismissHelpButtonList(guiParent, texts=texts, tipTexts=tipTexts, commands=commands, help_msg=self.help_msg, help_url=self.help_url, dismiss_text=self.dismiss_text, expands=True) buttons.grid(row=row, column=0, columnspan=2, sticky=Tkinter.EW) def save(self): projName = self.proj_name_entry.get() directory = self.proj_dir_select.getDirectory() directory = joinPath(directory, projName) if self.isProjectDirectory(directory): if not showOkCancel('Overwrite directory', 'Overwrite existing project directory?', parent=self): return self.updateInfo() self.did_save = False changeDataLocations = self.dataCheckButton.isSelected() done = False try: done = Io.saveProject(self.project, newPath=directory, newProjectName=projName, createFallback=True, showYesNo=showYesNo, changeDataLocations=changeDataLocations, showWarning=showWarning) if done: showInfo('Project saved', 'Project saved successfully') self.did_save = True if self.callback: self.callback(self.project) elif self.modal: return # give another chance except Implementation.ApiError, e: showError('Save project', e.error_msg) except IOError, e: showError('Save project', str(e))
class LinkResonancesPopup(TemporaryBasePopup): help_url = joinPath(getHelpUrlDir(), 'LinkResonances.html') def __init__(self, parent, formatNamesList, formatNamesDict, title='Link resonances setup'): self.guiParent = parent self.formatNamesList = formatNamesList self.formatNamesDict = formatNamesDict self.status = False TemporaryBasePopup.__init__(self, parent=parent, title=title, modal=False, transient=False) def body(self, master): row = 0 label = Label(master, text='File format reference:') label.grid(row=row, column=0, sticky=Tkinter.E) self.menu = PulldownMenu(master, entries=self.formatNamesList) self.menu.grid(row=row, column=1, sticky=Tkinter.E, ipadx=20) row = row + 1 label = Label(master, text='Try to link unrecognized atom names') label.grid(row=row, column=0, sticky=Tkinter.E) self.linkRes = CheckButton(master, selected=True) self.linkRes.grid(row=row, column=1, sticky=Tkinter.W) row = row + 1 label = Label(master, text='Try IUPAC names (as backup)') label.grid(row=row, column=0, sticky=Tkinter.E) self.iupacNames = CheckButton(master) self.iupacNames.grid(row=row, column=1, sticky=Tkinter.W) row = row + 1 label = Label(master, text='Use ambiguous name information') label.grid(row=row, column=0, sticky=Tkinter.E) self.useAmb = CheckButton(master) self.useAmb.grid(row=row, column=1, sticky=Tkinter.W) row = row + 1 label = Label(master, text='Complete stereospecific assignment') label.grid(row=row, column=0, sticky=Tkinter.E) self.allStereo = CheckButton(master) self.allStereo.grid(row=row, column=1, sticky=Tkinter.W) row = row + 1 label = Label(master, text='Force shift merges') label.grid(row=row, column=0, sticky=Tkinter.E) self.shiftMerge = CheckButton(master) self.shiftMerge.grid(row=row, column=1, sticky=Tkinter.W) row = row + 1 label = Label(master, text='Status other atom for all single prochiral atoms') label.grid(row=row, column=0, sticky=Tkinter.E) self.allSingleProchiral = PulldownMenu( master, entries=['Unknown', 'Same information', 'Always ignore']) self.allSingleProchiral.grid(row=row, column=1, sticky=Tkinter.W) row = row + 1 label = Label( master, text='Status other atom for all possibly equivalent single atoms') label.grid(row=row, column=0, sticky=Tkinter.E) self.allSinglePossEquiv = PulldownMenu( master, entries=['Unknown', 'Always equivalent', 'Always ignore']) self.allSinglePossEquiv.grid(row=row, column=1, sticky=Tkinter.W) row = row + 1 label = Label( master, text= 'Automatically connect ambiguous resonances to stereospecific ones' ) label.grid(row=row, column=0, sticky=Tkinter.E) self.connStereo = CheckButton(master) self.connStereo.grid(row=row, column=1, sticky=Tkinter.W) row = row + 1 label = Label(master, text='Use minimal number of popups') label.grid(row=row, column=0, sticky=Tkinter.E) self.minimalPopups = CheckButton(master) self.minimalPopups.grid(row=row, column=1, sticky=Tkinter.W) row = row + 1 label = Label(master, text='Verbose output') label.grid(row=row, column=0, sticky=Tkinter.E) self.verbose = CheckButton(master, selected=True) self.verbose.grid(row=row, column=1, sticky=Tkinter.W) row = row + 1 label = Label( master, text= 'Warning: it is recommended you save your project first,\nin case linkResonances is interrupted (this might corrupt the data).', fg='red') label.grid(row=row, column=0, columnspan=2, ipady=15, sticky=Tkinter.EW) row = row + 1 texts = ['Link resonances to atoms'] commands = [ self.ok ] # This calls 'ok' in BasePopup, this then calls 'apply' in here buttons = createDismissHelpButtonList(master, texts=texts, commands=commands, help_url=self.help_url) buttons.grid(row=row, column=0, columnspan=3) def apply(self): self.assignFormat = self.formatNamesDict[self.menu.getSelected()] self.globalStereoAssign = self.allStereo.isSelected() self.useIupacMatching = self.iupacNames.isSelected() self.useLinkResonancePopup = self.linkRes.isSelected() self.minimalPrompts = self.minimalPopups.isSelected() self.verbose = self.verbose.isSelected() self.forceShiftMerge = self.shiftMerge.isSelected() self.useAmbiguity = self.useAmb.isSelected() self.autoConnectStereo = self.connStereo.isSelected() self.setSingleProchiral = self.allSingleProchiral.getSelected() self.setSinglePossEquiv = self.allSinglePossEquiv.getSelected() self.status = True return True