class LabeledEntry(Frame): def __init__(self, parent, label, entry='', separator=': ', label_width=20, entry_width=60, label_anchor=Tkinter.E, show='', returnCallback=None, tipText=None, *args, **kw): apply(Frame.__init__, (self, parent) + args, kw) self.grid_columnconfigure(1, weight=1) self.separator = separator text = label + separator self.label = Label(self, text=text, width=label_width, anchor=label_anchor) self.label.grid(row=0, column=0, sticky=Tkinter.EW) self.entry = Entry(self, show=show, width=entry_width, returnCallback=returnCallback, tipText=tipText) self.entry.insert(0, entry) self.entry.grid(row=0, column=1, sticky=Tkinter.EW) def getLabel(self): text = self.label.get() n = text.find(self.separator) if (n >= 0): text = text[:n] return text def setLabel(self, text=''): text = text + self.separator self.label.set(text) def getEntry(self): return self.entry.get() def setEntry(self, text=''): self.entry.set(text)
class PCSPopup(BasePopup): def __init__(self, parent, *args, **kw): BasePopup.__init__(self, parent, *args, **kw) def body(self, guiFrame): spectraFrame = LabelFrame(guiFrame, text='Spectra', grid=(0, 0)) Label(spectraFrame, text='Diamagnetic Spectrum:', grid=(0, 0)) self.diaSpecPulldown = PulldownList(spectraFrame, callback=self.changeDiaSpec, grid=(0, 1)) Label(spectraFrame, text='Paramagnetic Spectrum:', grid=(1, 0)) self.paraSpecPulldown = PulldownList(spectraFrame, callback=self.changeParaSpec, grid=(1, 1)) pcsIOFrame = LabelFrame(guiFrame, text='PCS', grid=(1, 0)) Label(pcsIOFrame, text='Write Experimental PCS:', grid=(0, 0)) self.expPCSEntry = Entry(pcsIOFrame, text='pcsexp.npc', width=32, grid=(0, 1)) Button(pcsIOFrame, command=self.writePCSFileChange, grid=(0, 2), text='Choose File') Label(pcsIOFrame, text='Read Calculated PCS:', grid=(1, 0)) self.calPCSEntry = Entry(pcsIOFrame, text='pcscal.npc', width=32, grid=(1, 1)) Button(pcsIOFrame, command=self.readPCSFileChange, grid=(1, 2), text='Choose File') scriptFrame = LabelFrame(guiFrame, text='Paramagpy Script', grid=(2, 0)) Label(scriptFrame, text='PCS fitting script:', grid=(0, 0)) self.scriptEntry = Entry(scriptFrame, text='paramagpy_fit_pcs.py', width=32, grid=(0, 1)) Button(scriptFrame, command=self.writeScriptFileChange, grid=(0, 2), text='Choose File') commandFrame = LabelFrame(guiFrame, text='Commands', grid=(3, 0)) Button(commandFrame, command=self.writePCS, grid=(0, 0), text='Write PCS') Button(commandFrame, command=self.fitTensor, grid=(0, 1), text='Fit Tensor') Button(commandFrame, command=self.readPCS, grid=(0, 2), text='Read PCS') self.updateSpecPulldown() def updateSpecPulldown(self): spectra = ExperimentBasic.getSpectra(self.project) names = [] for spectrum in spectra: names.append("{}:{}".format(spectrum.experiment.name, spectrum.name)) self.diaSpecPulldown.setup(names, spectra, 0) self.paraSpecPulldown.setup(names, spectra, 0) if len(spectra) > 1: self.diaSpec = spectra[0] self.paraSpec = spectra[1] def changeDiaSpec(self, spectrum): self.diaSpec = spectrum def changeParaSpec(self, spectrum): self.paraSpec = spectrum def writePCSFileChange(self): fileTypes = [FileType('PCS', ['*.npc']), FileType('All', ['*'])] fileSelectPopup = FileSelectPopup( self, file=os.path.basename(self.expPCSEntry.get()), directory=os.path.dirname(self.expPCSEntry.get()), file_types=fileTypes, title='Save PCS values to file', dismiss_text='Cancel', selected_file_must_exist=False, multiSelect=False, ) self.expPCSEntry.set(fileSelectPopup.getFile()) def readPCSFileChange(self): fileTypes = [FileType('PCS', ['*.npc']), FileType('All', ['*'])] fileSelectPopup = FileSelectPopup( self, file=os.path.basename(self.calPCSEntry.get()), directory=os.path.dirname(self.calPCSEntry.get()), file_types=fileTypes, title='Save PCS values to file', dismiss_text='Cancel', selected_file_must_exist=False, multiSelect=False, ) self.calPCSEntry.set(fileSelectPopup.getFile()) def writeScriptFileChange(self): fileTypes = [FileType('python', ['*.py']), FileType('All', ['*'])] fileSelectPopup = FileSelectPopup( self, file=os.path.basename(self.scriptEntry.get()), directory=os.path.dirname(self.scriptEntry.get()), file_types=fileTypes, title='Paramagpy script', dismiss_text='Cancel', selected_file_must_exist=False, multiSelect=False, ) self.scriptEntry.set(fileSelectPopup.getFile()) def writePCS(self): diaPeaks = self.diaSpec.getActivePeakList() paraPeaks = self.paraSpec.getActivePeakList() deltaPeaks = self.subtract_peak_lists(diaPeaks, paraPeaks) self.write_npc(deltaPeaks) def readPCS(self): print(self.diaSpec.experiment.name) print(self.paraSpec.experiment.name) calPCSName = os.path.abspath(self.calPCSEntry.get()) if not os.path.isfile(calPCSName): showWarning( "PCS file not found", "Cound not find backcalculated PCS values. Please ensure the following file exists:\n{}" .format(calPCSName)) return pcs_values = {} with open(calPCSName) as o: for line in o: try: sequence, atomName, pcsValue, error = line.split() key = int(sequence), atomName pcs_values[key] = float(pcsValue) except ValueError: print("Line ignored in calculated PCS file:\n{}".format( line)) peakListDia = self.diaSpec.getActivePeakList() newPeakList = self.paraSpec.newPeakList(isSimulated=True) newPeakList.analysisPeakList.setSymbolColor("#FF0000") newPeakList.setDetails("Back-calculated PCS") for diaPeak in peakListDia.sortedPeaks(): paraPeak = newPeakList.newPeak() paraPeak.setAnnotation(diaPeak.getAnnotation()) for ddim, pdim in zip(diaPeak.sortedPeakDims(), paraPeak.sortedPeakDims()): pdim.setAnnotation(ddim.getAnnotation()) if not ddim.dataDimRef: paraPeak.delete() break contrib = ddim.findFirstPeakDimContrib() if not contrib: paraPeak.delete() break resonance = contrib.getResonance() if not resonance: paraPeak.delete() break if not resonance.resonanceSet: paraPeak.delete() break residue = getResonanceResidue(resonance) sequence = residue.seqCode atomName = resonance.getAssignNames()[0] key = sequence, atomName if key in pcs_values: newShift = ddim.getValue() + pcs_values[key] if self.within_axis(pdim, newShift): pdim.setValue(newShift) else: paraPeak.delete() break else: paraPeak.delete() break def fitTensor(self): scriptName = os.path.abspath(self.scriptEntry.get()) expPCSName = os.path.abspath(self.expPCSEntry.get()) if not os.path.isfile(scriptName): showWarning( "Script not found", "Could not find paramagpy script. Please ensure the following script exists:\n{}" .format(scriptName)) return if not os.access(scriptName, os.X_OK): showWarning( "Invalid permissions", "The paramagpy script does not have executable permissions. Please run in the terminal:\nchmod +x {}" .format(scriptName)) return if not os.path.isfile(expPCSName): showWarning( "PCS file not found", "Cound not find experimental PCS file.\nEnsure the following file exists:{}" .format(expPCSName)) return subprocess.call([scriptName, expPCSName]) def write_npc(self, pcsValues): fileName = os.path.abspath(self.expPCSEntry.get()) with open(fileName, 'w') as o: for key in sorted(pcsValues): seq, atom = key value = pcsValues[key] line = "{0:<4d}{1:<2}{2:15.8f} 0.0\n".format(seq, atom, value) o.write(line) o.close() showInfo("Wrote PCS", "Wrote experimental PCS to: {}".format(fileName)) @staticmethod def extract_peaks(peakList): out = [] for peak in peakList.sortedPeaks(): tmp = {} for peakDim in peak.sortedPeakDims(): if not peakDim.dataDimRef: continue contrib = peakDim.findFirstPeakDimContrib() if not contrib: continue resonance = contrib.getResonance() if not resonance: continue if not resonance.resonanceSet: continue residue = getResonanceResidue(resonance) sequence = residue.seqCode atomName = resonance.getAssignNames()[0] tmp[(sequence, atomName)] = peakDim out.append((peak, tmp)) return out @staticmethod def within_axis(peakDim, value): dataDim = peakDim.getDataDim() dr = dataDim.findFirstDataDimRef() diff = dr.spectralWidthOrig - dr.spectralWidth low = dr.refValue - dr.spectralWidth + dr.spectralWidthOrig / 2. high = dr.refValue + dr.spectralWidthOrig / 2. return low < value < high def subtract_peak_lists(self, diamagPeaks, paramagPeaks): dpeaks = self.extract_peaks(diamagPeaks) ppeaks = self.extract_peaks(paramagPeaks) dvals = {} pvals = {} for peak, dic in dpeaks: dvals.update(dic) for peak, dic in ppeaks: pvals.update(dic) out = {} for key in set(dvals) & set(pvals): out[key] = pvals[key].value - dvals[key].value return out
class BacusPopup(BasePopup): def __init__(self, parent, *args, **kw): self.project = parent.project self.hNoesy = None self.nNoesy = None self.cNoesy = None self.shiftList = None BasePopup.__init__(self, parent, title="BACUS control module", **kw) def body(self, guiParent): guiParent.grid_columnconfigure(0, weight=1) row = 0 guiParent.grid_rowconfigure(row, weight=1) inputFrame = LabelFrame(guiParent, text='Inputs:') inputFrame.grid(row=row, column=0, sticky=Tkinter.NSEW) label = Label(inputFrame, text='NOESY:') label.grid(row=0, column=0, sticky=Tkinter.NW) self.hNoesyPulldown = PulldownMenu(inputFrame,callback=self.setHNoesy) self.hNoesyPulldown.grid(row=0, column=1, sticky=Tkinter.NW) label = Label(inputFrame, text='15N NOESY:') label.grid(row=1, column=0, sticky=Tkinter.NW) self.nNoesyPulldown = PulldownMenu(inputFrame,callback=self.setNNoesy) self.nNoesyPulldown.grid(row=1, column=1, sticky=Tkinter.NW) label = Label(inputFrame, text='13C NOESY:') label.grid(row=2, column=0, sticky=Tkinter.NW) self.cNoesyPulldown = PulldownMenu(inputFrame,callback=self.setCNoesy) self.cNoesyPulldown.grid(row=2, column=1, sticky=Tkinter.NW) label = Label(inputFrame, text='Shift List:') label.grid(row=3, column=0, sticky=Tkinter.NW) self.shiftListPulldown = PulldownMenu(inputFrame,callback=self.setShiftList) self.shiftListPulldown.grid(row=3, column=1, sticky=Tkinter.NW) label = Label(inputFrame, text='2d BACUS executable:') label.grid(row=4, column=0, sticky=Tkinter.NW) self.executableEntry = Entry(inputFrame, text='/home/tjs23/clouds/justin/SpiBacusMidge/bacus/bacus_tjs.exe') self.executableEntry.grid(row=4, column=1, sticky=Tkinter.NW) self.executableButton = Button(inputFrame, text='Choose file', command=self.chooseExecutable) self.executableButton.grid(row=5, column=1, sticky=Tkinter.EW) row += 1 outputFrame = LabelFrame(guiParent, text='Output:') outputFrame.grid(row=row, column=0, sticky=Tkinter.NSEW) row += 1 texts = ['Run BACUS 2d','Run BACUS 3d'] commands = [self.runBacus,self.runBacus3d] self.bottomButtons = createDismissHelpButtonList(guiParent,texts=texts,commands=commands,expands=0,help_url=None) self.bottomButtons.grid(row=row, column=0, sticky=Tkinter.EW) self.update() def chooseExecutable(self): fileTypes = [FileType('Table', ['*.exe']), FileType('All', ['*'])] fileSelectPopup = FileSelectPopup(self, file_types = fileTypes, title = 'Choose 2d BACUS executable', dismiss_text = 'Cancel', selected_file_must_exist = False) fileName = fileSelectPopup.getFile() if fileName: self.executableEntry.set(fileName) def update(self): # set defaults via pulldown callbacks names = [self.getPeakListlabel(x) for x in self.gethNoesys()] if names: self.hNoesyPulldown.setup(names,0) else: self.hNoesyPulldown.setup([],-1) names = [self.getPeakListlabel(x) for x in self.getNNoesys()] if names: self.nNoesyPulldown.setup(names,0) else: self.nNoesyPulldown.setup([],-1) names = [self.getPeakListlabel(x) for x in self.getCNoesys()] if names: self.cNoesyPulldown.setup(names,0) else: self.cNoesyPulldown.setup([],-1) names = ['Shift List %d' % x.serial for x in self.getShiftLists()] if names: self.shiftListPulldown.setup(names,0) else: self.shiftListPulldown.setup([],-1) self.updateButtons() def updateButtons(self): if ( self.cNoesy or self.nNoesy ) and self.shiftList: self.bottomButtons.buttons[1].enable() else: self.bottomButtons.buttons[1].disable() if self.hNoesy and self.executableEntry.get() and self.shiftList: self.bottomButtons.buttons[0].enable() else: self.bottomButtons.buttons[0].disable() def runBacus3d(self, fileRoot='CCPN1'): if ( self.cNoesy or self.nNoesy ) and self.shiftList: run3dBacus(fileRoot,self.shiftList,self.cNoesy,self.nNoesy) def runBacus(self, fileRoot='CCPN1'): executable = self.executableEntry.get() if self.hNoesy and self.shiftList and executable: run2dBacus(fileRoot, executable, self.hNoesy, self.shiftList) def getPeakListlabel(self, peakList): return '%s:%s:%d' % (peakList.dataSource.experiment.name, peakList.dataSource.name, peakList.serial) def getShiftLists(self): shiftLists = [] if self.project: nmrProject = self.project.currentNmrProject shiftLists = nmrProject.findAllMeasurementLists(className='ShiftList') return list(shiftLists) def setShiftList(self, index, name=None): shiftLists = self.getShiftLists() if shiftLists: self.shiftList = shiftLists[index] else: self.shiftList = None def gethNoesys(self): peakLists = [] if self.project: for experiment in self.project.currentNmrProject.experiments: for spectrum in experiment.dataSources: isotopes = getSpectrumIsotopes(spectrum) if isotopes == ['1H', '1H']: for peakList in spectrum.peakLists: #if 'NOE' in experiment.name.upper(): # peakLists.insert(0, peakList) #else: peakLists.append(peakList) return peakLists def getNNoesys(self): peakLists = [] if self.project: for experiment in self.project.currentNmrProject.experiments: for spectrum in experiment.dataSources: isotopes = getSpectrumIsotopes(spectrum) isotopes.sort() if isotopes == ['15N', '1H', '1H']: for peakList in spectrum.peakLists: #if 'NOE' in experiment.name.upper(): # peakLists.insert(0, peakList) #else: peakLists.append(peakList) return peakLists def getCNoesys(self): peakLists = [] if self.project: for experiment in self.project.currentNmrProject.experiments: for spectrum in experiment.dataSources: isotopes = getSpectrumIsotopes(spectrum) isotopes.sort() if isotopes == ['13C', '1H', '1H']: for peakList in spectrum.peakLists: if 'NOE' in experiment.name.upper(): peakLists.insert(0, peakList) else: peakLists.append(peakList) return peakLists def setHNoesy(self, index, name=None): peakLists = self.gethNoesys() if peakLists: self.hNoesy = peakLists[index] else: self.hNoesy = None def setNNoesy(self, index, name=None): peakLists = self.getNNoesys() if peakLists: self.nNoesy = peakLists[index] else: self.nNoesy = None def setCNoesy(self, index, name=None): peakLists = self.getCNoesys() if peakLists: self.cNoesy = peakLists[index] else: self.cNoesy = None def destroy(self): BasePopup.destroy(self)
class NewWindowPopup(BasePopup): """ **Create New Windows to Display Spectra** This tool is used to make new windows for the graphical display of spectra, which will usually be as contours. It is notable that some spectrum windows will be made automatically when spectra are loaded if there is no existing appropriate window to display a spectrum. However, making new spectrum windows allows the user to specialise different windows for different tasks and gives complete freedom as to which types of axis go in which direction. For example the user may wish to make a new window so that a spectrum can be viewed from an orthogonal, rotated aspect. A new spectrum window is made by first considering whether it is similar to any existing windows. If so, then the user selects the appropriate template window to base the new one upon. The user then chooses a name for the window via the "New window name" field, although the name may be changed after the window is created. Usually the user does not need to consider the "Strips" section, but if required the new window can be created with starting strips and orthogonal sub-divisions (although these are not permanent). After setting the required axes and spectrum visibility, as described below, the user clicks on [Create Window!] to actually make the new spectrum display window. **Axes** The number and type of axes for the new window are chosen using the pulldown menus in the "Axes" section. The idea is that the user chooses which NMR isotopes should appear on the X, Y, & Z axes. Naturally, X and Y axes must always be set to something, to represent the plane of the screen, but the Z axes are optional. If not required, a Z axis may be set to "None" to indicate that it will not be used. Up to four Z axes may be specified, labelled as "z1", "z2" etc., and these represent extra dimensions orthogonal to the plane of the screen, which are often conceptualised as depth axes. It should be noted that the Y axis type may be set to "value", which refers to a spectrum intensity axis, rather than an NMR isotope axis. Setting the Y axis to "value" and only having the X axis set to an isotope is used to create windows that can show 1D spectra. Such value axes can also be used for 2D and higher dimensionality spectra, to show the data as an intensity graph (a "slice") rather than as contours. **Spectra** The lower "Viewed Spectra" section lists all of the spectra within the project that may be shown by a window with the selected axes. All spectra with isotopes in their data dimensions that match the isotope types of the window axes can potentially be displayed. This system also allows for displayed spectra to have fewer dimensions than the axes has windows, as long as at least the X and Y axes are present. For example a 2D H-N spectrum can be shown in a 3D H-N-H window, but not a 3D H-H-N. For spectra that have more than one data dimension of the same isotope, then which data dimension goes with which window axis is not always known to Analysis. Where there is ambiguity, this system will simply map the spectrum data dimensions in order to the next matching window axis. If this mapping turns out to be wrong, then it may be changed at any time via the main _Windows settings; toggling the "Dim. Mapping" of the "Spectrum & Peak List Mappings" tab. For the spectra listed in the lower table, which may be placed in the new window, the user has control over whether the spectra actually will appear. Firstly the user can change the "Visible?" column, either via a double-click or by using the appropriate lower buttons. By default spectra are set as not being visible in new windows, and the user toggles the ones that should be seen to "Yes". This basic spectrum visibility can readily be changed by the toggle buttons that appear in the "toolbar" at the top of the spectrum display, so the "Visible?" setting here is only about what initially appears. The "In Toolbar?" setting of a spectrum is a way of allowing the user to state that a spectrum should never appear in the window, and not even allow it to be toggled on later via the toolbar at the top of the windows. This is a way of reducing clutter, and allows certain windows to be used for particular subsets of spectra. For example the user may wish to put the spectra for a temperature series in one window, but not in other windows used for resonance assignment where they would get in the way. The "In Toolbar" setting can be changed after a window has been made, but only via the main Windows_ settings popup. .. _Windows: EditWindowPopup.html """ def __init__(self, parent, *args, **kw): self.visibleSpectra = parent.visibleSpectra self.toolbarSpectra = parent.toolbarSpectra self.waiting = False self.window = None BasePopup.__init__(self, parent=parent, title='Window : New Window', **kw) def body(self, guiFrame): guiFrame.grid_columnconfigure(2, weight=1) row = 0 label = Label(guiFrame, text='Template window: ', grid=(row, 0)) tipText = 'Selects which window to use as the basis for making a new spectrum window; sets the axis types accordingly' self.window_list = PulldownList(guiFrame, grid=(row, 1), callback=self.setAxisTypes, tipText=tipText) frame = LabelFrame(guiFrame, text='Strips', grid=(row, 2), gridSpan=(2, 1)) buttons = UtilityButtonList(guiFrame, doClone=False, helpUrl=self.help_url, grid=(row, 3)) row += 1 label = Label(guiFrame, text='New window name: ', grid=(row, 0)) tipText = 'A short name to identify the spectrum window, which will appear in the graphical interface' self.nameEntry = Entry(guiFrame, width=16, grid=(row, 1), tipText=tipText) row += 1 label = Label(frame, text='Columns: ', grid=(0, 0)) tipText = 'The number of vertical strips/dividers to initially make in the spectrum window' self.cols_menu = PulldownList(frame, objects=STRIP_NUMS, grid=(0, 1), texts=[str(x) for x in STRIP_NUMS], tipText=tipText) label = Label(frame, text='Rows: ', grid=(0, 2)) tipText = 'The number of horizontal strips/dividers to initially make in the spectrum window' self.rows_menu = PulldownList(frame, objects=STRIP_NUMS, grid=(0, 3), texts=[str(x) for x in STRIP_NUMS], tipText=tipText) row += 1 div = LabelDivider(guiFrame, text='Axes', grid=(row, 0), gridSpan=(1, 4)) row += 1 self.axis_lists = {} frame = Frame(guiFrame, grid=(row, 0), gridSpan=(1, 4)) col = 0 self.axisTypes = {} self.axisTypesIncludeNone = {} for label in AXIS_LABELS: self.axisTypes[label] = None w = Label(frame, text=' ' + label) w.grid(row=0, column=col, sticky='w') col += 1 if label in ('x', 'y'): includeNone = False tipText = 'Sets the kind of measurement (typically ppm for a given isotope) that will be used along the window %s axis' % label else: includeNone = True tipText = 'Where required, sets the kind of measurement (typically ppm for a given isotope) that will be used along the window %s axis' % label self.axisTypesIncludeNone[label] = includeNone getAxisTypes = lambda label=label: self.getAxisTypes(label) callback = lambda axisType, label=label: self.changedAxisType( label, axisType) self.axis_lists[label] = PulldownList(frame, callback=callback, tipText=tipText) self.axis_lists[label].grid(row=0, column=col, sticky='w') col += 1 frame.grid_columnconfigure(col, weight=1) row += 1 div = LabelDivider(guiFrame, text='Viewed Spectra', grid=(row, 0), gridSpan=(1, 4)) row += 1 guiFrame.grid_rowconfigure(row, weight=1) editWidgets = [None, None, None, None] editGetCallbacks = [None, self.toggleVisible, self.toggleToolbar, None] editSetCallbacks = [None, None, None, None] tipTexts = [ 'The "experiment:spectrum" name for the spectrum that may be viewed in the new window, given the axis selections', 'Sets whether the spectrum contours will be visible in the new window', 'Sets whether the spectrum appears at all in the window; if not in the toolbar it cannot be displayed', 'The number of peak lists the spectrum contains' ] headingList = ['Spectrum', 'Visible?', 'In Toolbar?', 'Peak Lists'] self.scrolledMatrix = ScrolledMatrix(guiFrame, headingList=headingList, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, multiSelect=True, grid=(row, 0), gridSpan=(1, 4), tipTexts=tipTexts) row += 1 tipTexts = [ 'Creates a new spectrum window with the specified parameters', 'Sets the contours of the selected spectra to be visible when the new window is made', 'Sets the contours of the selected spectra to not be displayed when the new window is made', 'Sets the selected spectra as absent from the window toolbar, and thus not displayable at all' ] texts = [ 'Create Window!', 'Selected\nVisible', 'Selected\nNot Visible', 'Selected\nNot In Toolbar' ] commands = [ self.ok, self.setSelectedDisplay, self.setSelectedHide, self.setSelectedAbsent ] buttonList = ButtonList(guiFrame, texts=texts, grid=(row, 0), commands=commands, gridSpan=(1, 4), tipTexts=tipTexts) buttonList.buttons[0].config(bg='#B0FFB0') self.updateAxisTypes() self.updateWindow() self.updateWindowName() self.administerNotifiers(self.registerNotify) self.updateAfter() def administerNotifiers(self, notifyFunc): self.registerNotify(self.updateWindowName, 'ccpnmr.Analysis.SpectrumWindow', '__init__') for clazz in ('ccp.nmr.Nmr.Experiment', 'ccp.nmr.Nmr.DataSource'): for func in ('__init__', 'delete', 'setName'): notifyFunc(self.updateAfter, clazz, func) for func in ('__init__', 'delete'): notifyFunc(self.updateAfter, 'ccp.nmr.Nmr.PeakList', func) for func in ('__init__', 'delete', 'setName', 'addSpectrumWindowGroup', 'removeSpectrumWindowGroup', 'setSpectrumWindowGroups'): notifyFunc(self.updateWindow, 'ccpnmr.Analysis.SpectrumWindow', func) for func in ('addSpectrumWindow', 'removeSpectrumWindow', 'setSpectrumWindows'): notifyFunc(self.updateWindow, 'ccpnmr.Analysis.SpectrumWindowGroup', func) for func in ('__init__', 'delete', 'setName'): notifyFunc(self.updateAxisTypes, 'ccpnmr.Analysis.AxisType', func) # Set visible contours, on commit, according to selection # Get hold of spectrumWindowView ASAP def destroy(self): self.administerNotifiers(self.unregisterNotify) BasePopup.destroy(self) def getSpectra(self): spectrumIsotopes = {} spectra = [] for experiment in self.nmrProject.sortedExperiments(): name = experiment.name for spectrum in experiment.sortedDataSources(): spectrumIsotopes[spectrum] = [] spectra.append(['%s:%s' % (name, spectrum.name), spectrum]) for dataDim in spectrum.dataDims: dimTypes = [] if dataDim.className != 'SampledDataDim': for dataDimRef in dataDim.dataDimRefs: expDimRef = dataDimRef.expDimRef isotopes = set() for isotopeCode in expDimRef.isotopeCodes: isotopes.add(isotopeCode) dimTypes.append( (expDimRef.measurementType.lower(), isotopes)) else: dimTypes.append('sampled') spectrumIsotopes[spectrum].append(dimTypes) axisIsotopes = {} for label in AXIS_LABELS: if label not in ('x', 'y'): if self.axis_lists[label].getSelectedIndex() == 0: continue axisType = self.axis_lists[label].getObject() axisIsotopes[label] = (axisType.measurementType.lower(), set(axisType.isotopeCodes)) spectraSel = [] axes = axisIsotopes.keys() axes.sort() for name, spectrum in spectra: dimIsotopes = spectrumIsotopes[spectrum] for label in axes: mType, selected = axisIsotopes[label] if label == 'y' and mType == 'none': continue # value axis for i, dimTypes in enumerate(dimIsotopes): for dimType in dimTypes: if dimType == 'sampled': if label != 'z1': continue axisType = self.axis_lists[label].getObject() if axisType.name != 'sampled': continue dimIsotopes.pop(i) break else: measurementType, isotopes = dimType if (mType == measurementType) and (selected <= isotopes): dimIsotopes.pop(i) break else: continue break else: if label in ('x', 'y'): break else: if not dimIsotopes: spectraSel.append([name, spectrum]) return spectraSel def setSelectedAbsent(self): for spectrum in self.scrolledMatrix.currentObjects: self.visibleSpectra[spectrum] = False self.toolbarSpectra[spectrum] = False self.updateAfter() def setSelectedDisplay(self): for spectrum in self.scrolledMatrix.currentObjects: self.visibleSpectra[spectrum] = True self.toolbarSpectra[spectrum] = True self.updateAfter() def setSelectedHide(self): for spectrum in self.scrolledMatrix.currentObjects: self.visibleSpectra[spectrum] = False self.toolbarSpectra[spectrum] = True self.updateAfter() def toggleToolbar(self, spectrum): boolean = not self.toolbarSpectra.get(spectrum, True) self.toolbarSpectra[spectrum] = boolean if boolean is False: self.visibleSpectra[spectrum] = False self.updateAfter() def toggleVisible(self, spectrum): boolean = not self.visibleSpectra.get(spectrum, False) self.visibleSpectra[spectrum] = boolean if boolean: if not self.toolbarSpectra.get(spectrum, True): self.toolbarSpectra[spectrum] = True self.updateAfter() def updateAfter(self, object=None): if self.waiting: return else: self.waiting = True self.after_idle(self.update) def update(self): for spectrum in self.visibleSpectra.keys(): if spectrum.isDeleted: del self.visibleSpectra[spectrum] textMatrix = [] objectList = [] colorMatrix = [] for name, spectrum in self.getSpectra(): colours = [None, None, None, None] if self.visibleSpectra.get(spectrum): colours[0] = '#60F060' isVisible = 'Yes' self.visibleSpectra[ spectrum] = True # do not need this but play safe in case above if changed else: isVisible = 'No' self.visibleSpectra[spectrum] = False if self.toolbarSpectra.get(spectrum, True): inToolbar = 'Yes' self.toolbarSpectra[spectrum] = True else: colours[0] = '#600000' inToolbar = 'No' self.toolbarSpectra[spectrum] = False datum = [ name, isVisible, inToolbar, ','.join(['%d' % pl.serial for pl in spectrum.peakLists]) ] textMatrix.append(datum) objectList.append(spectrum) colorMatrix.append(colours) self.scrolledMatrix.update(objectList=objectList, textMatrix=textMatrix, colorMatrix=colorMatrix) self.waiting = False def updateAxisTypes(self, *extra): for label in AXIS_LABELS: names = [] objects = [] includeNone = self.axisTypesIncludeNone[label] if includeNone: names.append('None') objects.append(None) axisType = self.axisTypes[label] axisTypes = self.getAxisTypes(label) objects.extend(axisTypes) if axisTypes: if axisType not in objects: axisType = objects[0] index = objects.index(axisType) names.extend([x.name for x in axisTypes]) else: index = 0 self.axis_lists[label].setup(names, objects, index) self.changedAxisType(label, axisType) def changedAxisType(self, label, axisType): if axisType is not self.axisTypes[label]: self.axisTypes[label] = axisType self.updateAfter() def updateWindow(self, *extra): window = self.window windows = self.parent.getWindows() if windows: if window not in windows: window = windows[0] index = windows.index(window) names = [x.name for x in windows] else: index = 0 names = [] self.window_list.setup(names, windows, index) self.setAxisTypes(window) def updateWindowName(self, *extra): self.nameEntry.set(WindowBasic.defaultWindowName(self.project)) def getAxisTypes(self, label): axisTypes = self.parent.getAxisTypes() if label == 'z1': axisTypes = [ axisType for axisType in axisTypes if axisType.name == 'sampled' or not axisType.isSampled ] else: axisTypes = [ axisType for axisType in axisTypes if not axisType.isSampled ] if label != 'y': axisTypes = [ at for at in axisTypes if at.name != WindowBasic.VALUE_AXIS_NAME ] return axisTypes def setAxisTypes(self, window): project = self.project if not project: return if window is self.window: return self.window = window if window: windowPanes = window.sortedSpectrumWindowPanes() if windowPanes: # might be empty because notifier called before windowPanes set up windowPane = windowPanes[0] for label in AXIS_LABELS: axisPanel = windowPane.findFirstAxisPanel(label=label) if axisPanel and axisPanel.panelType \ and axisPanel.panelType.axisType: self.axis_lists[label].setSelected( axisPanel.panelType.axisType) elif label not in ('x', 'y'): self.axis_lists[label].setIndex(0) self.updateAfter() def apply(self): project = self.project if not project: return False name = self.nameEntry.get().strip() if not name: showError('No name', 'Need to enter name', parent=self) return False names = [ window.name for window in self.analysisProject.spectrumWindows ] if (name in names): showError('Repeated name', 'Name already used', parent=self) return False axisTypes = [] for label in AXIS_LABELS: if label not in ('x', 'y'): if self.axis_lists[label].getSelectedIndex() == 0: continue axisType = self.axis_lists[label].getObject() axisTypes.append(axisType) ncols = self.cols_menu.getObject() nrows = self.rows_menu.getObject() window = WindowBasic.createSpectrumWindow(project, name, [ axisTypes, ], ncols=ncols, nrows=nrows) return True
class MeccanoPopup(BasePopup): def __init__(self, parent, project, *args, **kw): self.alignMedium = None self.chain = None self.constraint = None self.constraintSet = None self.molSystem = None self.project = project self.run = None self.shiftList = None self.tensor = None BasePopup.__init__(self, parent=parent, title='MECCANO', *args, **kw) self.curateNotifiers(self.registerNotify) def body(self, guiFrame): guiFrame.grid_columnconfigure(0, weight=1) guiFrame.grid_rowconfigure(0, weight=1) options = ['Parameters','Restraints','Alignment Media & Tensors','About Meccano'] tabbedFrame = TabbedFrame(guiFrame, options=options) tabbedFrame.grid(row=0, column=0, sticky='nsew') frameA, frameB, frameC, frameD = tabbedFrame.frames frameA.grid_columnconfigure(1, weight=1) frameA.grid_rowconfigure(13, weight=1) frameB.grid_columnconfigure(1, weight=1) frameB.grid_rowconfigure(1, weight=1) frameC.grid_columnconfigure(0, weight=1) frameC.grid_rowconfigure(1, weight=1) frameD.grid_columnconfigure(0, weight=1) frameD.grid_rowconfigure(0, weight=1) texts = ['Run MECCANO!'] commands = [self.runMeccano] bottomButtons = createDismissHelpButtonList(guiFrame, texts=texts, commands=commands, expands=True) bottomButtons.grid(row=1, column=0, sticky='ew') if not Meccano: bottomButtons.buttons[0].disable() # Parameters row = 0 label = Label(frameA, text='Calculation Run:') label.grid(row=row,column=0,sticky='w') self.runPulldown = PulldownList(frameA, callback=self.selectRun) self.runPulldown.grid(row=row,column=1,sticky='w') row += 1 label = Label(frameA, text='Shift List (for CO):') label.grid(row=row,column=0,sticky='w') self.shiftListPulldown = PulldownList(frameA, callback=self.selectShiftList) self.shiftListPulldown.grid(row=row,column=1,sticky='w') row += 1 label = Label(frameA, text='Keep Copy of Used Shifts:') label.grid(row=row,column=0,sticky='w') self.toggleCopyShifts = CheckButton(frameA) self.toggleCopyShifts.grid(row=row,column=1,sticky='w') self.toggleCopyShifts.set(True) row += 1 label = Label(frameA, text='Molecular System:') label.grid(row=row,column=0,sticky='w') self.molSystemPulldown = PulldownList(frameA, callback=self.selectMolSystem) self.molSystemPulldown.grid(row=row,column=1,sticky='w') row += 1 label = Label(frameA, text='Chain:') label.grid(row=row,column=0,sticky='w') self.chainPulldown = PulldownList(frameA, callback=self.selectChain) self.chainPulldown.grid(row=row,column=1,sticky='w') self.chainPulldown.bind('<Leave>', self.updateRunParams) row += 1 label = Label(frameA, text='First Peptide Plane:') label.grid(row=row,column=0,sticky='w') self.firstResEntry = IntEntry(frameA, text=None, width=8) self.firstResEntry.grid(row=row,column=1,sticky='w') self.firstResEntry.bind('<Leave>', self.updateRunParams) row += 1 label = Label(frameA, text='Last Peptide Plane:') label.grid(row=row,column=0,sticky='w') self.lastResEntry = IntEntry(frameA, text=None, width=8) self.lastResEntry.grid(row=row,column=1,sticky='w') self.lastResEntry.bind('<Leave>', self.updateRunParams) row += 1 label = Label(frameA, text='Max Num Optimisation Steps:') label.grid(row=row,column=0,sticky='w') self.maxOptStepEntry = IntEntry(frameA, text=500, width=8) self.maxOptStepEntry.grid(row=row,column=1,sticky='w') self.maxOptStepEntry.bind('<Leave>', self.updateRunParams) row += 1 label = Label(frameA, text='Num Optimisation Peptide Planes:') label.grid(row=row,column=0,sticky='w') self.numOptPlaneEntry = IntEntry(frameA, text=2, width=8) self.numOptPlaneEntry.grid(row=row,column=1,sticky='w') self.numOptPlaneEntry.bind('<Leave>', self.updateRunParams) row += 1 label = Label(frameA, text='Min Num Optimisation Hits:') label.grid(row=row,column=0,sticky='w') self.numOptHitsEntry = IntEntry(frameA, text=5, width=8) self.numOptHitsEntry.grid(row=row,column=1,sticky='w') self.numOptHitsEntry.bind('<Leave>', self.updateRunParams) row += 1 label = Label(frameA, text='File Name Prefix:') label.grid(row=row,column=0,sticky='w') self.pdbFileEntry = Entry(frameA, text='Meccano', width=8) self.pdbFileEntry.grid(row=row,column=1,sticky='w') self.pdbFileEntry.bind('<Leave>', self.updateRunParams) row += 1 label = Label(frameA, text='Write Output File (.out):') label.grid(row=row,column=0,sticky='w') self.toggleWriteOutFile = CheckButton(frameA) self.toggleWriteOutFile.grid(row=row,column=1,sticky='w') self.toggleWriteOutFile.set(False) self.toggleWriteOutFile.bind('<Leave>', self.updateRunParams) row += 1 label = Label(frameA, text='Write PDB File (.pdb):') label.grid(row=row,column=0,sticky='w') self.toggleWritePdbFile = CheckButton(frameA) self.toggleWritePdbFile.grid(row=row,column=1,sticky='w') self.toggleWritePdbFile.set(True) self.toggleWritePdbFile.bind('<Leave>', self.updateRunParams) if not Meccano: row += 1 label = Label(frameA, text='The Meccano executable is not available (it needs to be compiled)', fg='red') label.grid(row=row,column=0,columnspan=2,sticky='w') # Restraints label = Label(frameB, text='Constraint Set:') label.grid(row=0,column=0,sticky='w') self.constraintSetPulldown = PulldownList(frameB, callback=self.selectConstraintSet) self.constraintSetPulldown.grid(row=0,column=1,sticky='w') self.alignMediumPulldown= PulldownList(self, callback=self.setAlignMedium) headingList = ['#','List Type','Use?','Alignment\nMedium','Num\nRestraints'] editWidgets = [None,None,None,self.alignMediumPulldown,None] editGetCallbacks = [None,None,self.toggleUseRestraints,self.getAlignMedium,None] editSetCallbacks = [None,None,None,self.setAlignMedium,None] self.restraintMatrix = ScrolledMatrix(frameB, headingList=headingList, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, callback=None, multiSelect=True) self.restraintMatrix.grid(row=1,column=0,columnspan=2,sticky='nsew') # Alignment Media div = LabelDivider(frameC,text='Alignment Media') div.grid(row=0,column=0,sticky='ew') self.mediumNameEntry = Entry(self, returnCallback=self.setMediumName) self.mediumDetailsEntry = Entry(self, returnCallback=self.setMediumDetails) headingList = ['#','Name','Details','Static Tensor','Dynamic Tensor'] editWidgets = [None, self.mediumNameEntry, self.mediumDetailsEntry, None, None] editGetCallbacks = [None, self.getMediumName, self.getMediumDetails, None, None] editSetCallbacks = [None, self.setMediumName, self.setMediumDetails, None, None] self.mediaMatrix = ScrolledMatrix(frameC, headingList=headingList, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, callback=self.selectAlignMedium, multiSelect=True) self.mediaMatrix.grid(row=1,column=0,sticky='nsew') texts = ['Add Alignment medium','Remove Alignment Medium'] commands = [self.addAlignMedium,self.removeAlignMedium] buttonList = ButtonList(frameC, texts=texts, commands=commands, expands=True) buttonList.grid(row=2,column=0,sticky='nsew') self.editAxialEntry = FloatEntry(self, returnCallback=self.setAxial) self.editRhombicEntry = FloatEntry(self, returnCallback=self.setRhombic) self.editAlphaEulerEntry = FloatEntry(self, returnCallback=self.setEulerAlpha) self.editBetaEulerEntry = FloatEntry(self, returnCallback=self.setEulerBeta) self.editGammaEulerEntry = FloatEntry(self, returnCallback=self.setEulerGamma) div = LabelDivider(frameC,text='Alignment Tensors') div.grid(row=3,column=0,sticky='ew') headingList = ['Type', u'Axial (\u03B6)',u'Rhombic (\u03B7)', u'Euler \u03B1',u'Euler \u03B2',u'Euler \u03B3'] editWidgets = [None,self.editAxialEntry, self.editRhombicEntry,self.editAlphaEulerEntry, self.editBetaEulerEntry,self.editGammaEulerEntry] editSetCallbacks = [None,self.setAxial,self.setRhombic, self.setEulerAlpha,self.setEulerBeta,self.setEulerGamma] editGetCallbacks = [None,self.getAxial,self.getRhombic, self.getEulerAlpha,self.getEulerBeta,self.getEulerGamma] self.tensorMatrix = ScrolledMatrix(frameC, maxRows=2, headingList=headingList, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, callback=self.selectTensor, multiSelect=True) self.tensorMatrix.grid(row=4,column=0,sticky='nsew') texts = ['Add Static Tensor','Add Dynamic Tensor','Remove Tensor'] commands = [self.addStaticTensor,self.addDynamicTensor,self.removeTensor] buttonList = ButtonList(frameC,texts=texts, commands=commands, expands=True) buttonList.grid(row=5,column=0,sticky='ew') # About label = Label(frameD, text='About Meccano...') label.grid(row=0,column=0,sticky='w') # self.geometry('500x400') self.updateShiftLists() self.updateMolSystems() self.updateResidueRanges() self.updateConstraintSets() self.updateAlignMedia() self.updateRuns() def close(self): self.updateRunParams() BasePopup.close(self) def destroy(self): self.updateRunParams() self.curateNotifiers(self.unregisterNotify) BasePopup.destroy(self) def curateNotifiers(self, notifyFunc): for func in ('__init__', 'delete'): notifyFunc(self.updateConstraintSetsAfter, 'ccp.nmr.NmrConstraint.NmrConstraintStore', func) for func in ('__init__', 'delete','setName','setConditionState'): for clazz in ('ccp.nmr.NmrConstraint.CsaConstraintList', 'ccp.nmr.NmrConstraint.DihedralConstraintList', 'ccp.nmr.NmrConstraint.DistanceConstraintList', 'ccp.nmr.NmrConstraint.HBondConstraintList', 'ccp.nmr.NmrConstraint.JCouplingConstraintList', 'ccp.nmr.NmrConstraint.RdcConstraintList'): notifyFunc(self.updateConstraintListsAfter, clazz, func) for func in ('__init__', 'delete',): for clazz in ('ccp.nmr.NmrConstraint.CsaConstraint', 'ccp.nmr.NmrConstraint.DihedralConstraint', 'ccp.nmr.NmrConstraint.DistanceConstraint', 'ccp.nmr.NmrConstraint.HBondConstraint', 'ccp.nmr.NmrConstraint.JCouplingConstraint', 'ccp.nmr.NmrConstraint.RdcConstraint'): notifyFunc(self.updateConstraintsAfter, clazz, func) for func in ('__init__', 'delete'): notifyFunc(self.updateShiftListsAfter,'ccp.nmr.Nmr.ShiftList', func) for func in ('__init__', 'delete'): notifyFunc(self.updateMolSystemsAfter,'ccp.molecule.MolSystem.MolSystem', func) for func in ('__init__', 'delete'): notifyFunc(self.updateChainsAfter,'ccp.molecule.MolSystem.Chain', func) for func in ('__init__', 'delete','setDynamicAlignment', 'setStaticAlignment','setName','setDetails'): notifyFunc(self.updateAlignMediaAfter,'ccp.nmr.NmrConstraint.ConditionState', func) def updateAlignMediaAfter(self, alignMedium): if alignMedium.nmrConstraintStore is self.constraintSet: self.after_idle(self.updateAlignMedia) if alignMedium is self.alignMedium: self.after_idle(self.updateTensors) def updateConstraintSetsAfter(self, constraintSet): self.after_idle(self.updateConstraintSets) def updateShiftListsAfter(self, shiftList): self.after_idle(self.updateShiftLists) def updateMolSystemsAfter(self, molSystem): self.after_idle(self.updateMolSystems) def updateChainsAfter(self, chain): self.after_idle(self.updateChains) def updateConstraintsAfter(self, constraint): if constraint.parent.parent is self.constraintSet: self.after_idle(self.updateConstraintLists) def updateConstraintListsAfter(self, constraintList): if constraintList.parent is self.constraintSet: self.after_idle(self.updateConstraintLists) def runMeccano(self): # # # Input checks first # # warning = '' if not self.molSystem: warning += 'No molecular system selected\n' if not self.chain: warning += 'No chain selected\n' if not self.constraintSet: warning += 'No selected constraint set\n' else: constraintLists = [cl for cl in self.constraintSet.constraintLists if cl.useForMeccano] if not constraintLists: warning += 'No constraint lists selected for use\n' first, last = self.updateResidueRanges() if (last-first) < 2: warning += 'Too few peptide planes selected\n' if warning: showWarning('Cannot run MECCANO','Encountered the following problems:\n' + warning) return if not self.run: self.run = self.makeSimRun() self.updateRunParams() if self.toggleCopyShifts.get() and self.shiftList: shiftList = self.run.findFirstOutputMeasurementList(className='ShiftList') if not shiftList: shiftList = self.project.currentNmrProject.newShiftList(name='Meccano Input') self.run.setOutputMeasurementLists([shiftList,]) shiftDict = {} for shift in shiftList.shifts: shiftDict[shift.resonance] = shift for shift in self.shiftList.shifts: resonance = shift.resonance resonanceSet = resonance.resonanceSet if resonanceSet: atom = resonanceSet.findFirstAtomSet().findFirstAtom() if (atom.name == 'C') and (atom.residue.molResidue.molType == 'protein'): shift2 = shiftDict.get(resonance) if shift2: shift2.value = shift.value shift2.error = shift.error else: shiftList.newShift(resonance=resonance, value=shift.value, error=shift.error) # # # Accumulate data from CCPN data model & GUI # # # Sequence residues = self.chain.sortedResidues() residueDict = {} seqData = [] for residue in residues: molResidue = residue.molResidue code1Letter = molResidue.chemComp.code1Letter if not code1Letter: msg = 'Encountered non-standard residue type: %s' showWarning('Cannot run MECCANO', msg % residue.ccpCode) return seqCode = residue.seqCode seqData.append((seqCode, code1Letter)) residueDict[seqCode] = residue.chemCompVar.chemComp.code3Letter # Media, RDCs & Dihedrals rdcLists = [] dihedralLists = [] for constraintList in constraintLists: if constraintList.className == 'RdcConsraintList': if constraintList.conditionState: rdcLists.append(constraintList) elif constraintList.className == 'DihedralConstraintList': dihedralLists.append(dihedralLists) f = PI_OVER_180 mediaData = [] for constraintList in rdcLists: medium = constraintList.conditionState dynamicTensor = medium.dynamicAlignment staticTensor = medium.staticAlignment if not (dynamicTensor or staticTensor): continue if dynamicTensor: dynamicTensorData = ['Dynamic', dynamicTensor.aAxial, dynamicTensor.aRhombic, f*dynamicTensor.alpha, f*dynamicTensor.beta, f*dynamicTensor.gamma] if staticTensor: staticTensorData = ['Static', staticTensor.aAxial, staticTensor.aRhombic, f*staticTensor.alpha, f*staticTensor.beta, f*staticTensor.gamma] if not dynamicTensor: dynamicTensorData = staticTensorData elif not staticTensor: staticTensorData = dynamicTensorData rdcData = [] for restraint in constraintList.constraints: items = list(restraint.items) if len(items) != 1: continue resonanceA, resonanceB = [fr.resonance for fr in items[0].resonances] resonanceSetA = resonanceA.resonanceSet if not resonanceSetA: continue resonanceSetB = resonanceB.resonanceSet if not resonanceSetB: continue resNameA = getResonanceName(resonanceA) resNameB = getResonanceName(resonanceB) residueA = resonanceSetA.findFirstAtomSet().findFirstAtom().residue residueB = resonanceSetB.findFirstAtomSet().findFirstAtom().residue seqA = residueA.seqCode seqB = residueB.seqCode value = restraint.targetValue error = restraint.error if error is None: key = [resNameA,resNameB] key.sort() sigma = DEFAULT_ERRORS.get(tuple(key), 1.0) rdcData.append([seqA, resNameA, seqB, resNameB, value, error]) mediaData.append((dynamicTensorData,staticTensorData,rdcData)) oneTurn = 360.0 dihedralDict = {} for constraintList in dihedralLists: for restraint in constraintList.constraints: items = list(restraint.items) if len(items) != 1: continue item = items[0] resonances = [fr.resonance for fr in item.resonances] resonanceSets = [r.resonanceSet for r in resonances] if None in resonanceSets: continue atoms = [rs.findFirstAtomSet().findFirstAtom() for rs in resonanceSets] atomNames = [a.name for a in atoms] if atomNames == PHI_ATOM_NAMES: isPhi = True elif atomNames == PSI_ATOM_NAMES: isPhi = False else: continue residue = atoms[2].residue if residue.chain is not self.chain: continue if isPhi: residuePrev = getLinkedResidue(residue, linkCode='prev') if not residuePrev: continue atomC0 = residuePrev.findFirstAtom(name='C') atomN = residue.findFirstAtom(name='N') atomCa = residue.findFirstAtom(name='CA') atomC = residue.findFirstAtom(name='C') atoms2 = [atomC0, atomN, atomCa, atomC] else: residueNext = getLinkedResidue(residue, linkCode='next') if not residueNext: continue atomN = residue.findFirstAtom(name='N') atomCa = residue.findFirstAtom(name='CA') atomC = residue.findFirstAtom(name='C') atomN2 = residueNext.findFirstAtom(name='N') atoms2 = [atomN, atomCa, atomC, atomN2] if atoms != atoms2: continue value = item.targetValue error = item.error if error is None: upper = item.upperLimit lower = item.lowerLimit if (upper is not None) and (lower is not None): dUpper = angleDifference(value, lower, oneTurn) dLower = angleDifference(upper, value, oneTurn) error = max(dUpper, dLower) elif lower is not None: error = angleDifference(value, lower, oneTurn) elif upper is not None: error = angleDifference(upper, value, oneTurn) else: error = 30.0 # Arbitrary, but sensible for TALOS, DANGLE seqCode = residue.seqCode if not dihedralDict.has_key(seqCode): dihedralDict[seqCode] = [None, None, None, None] # Phi, Psi, PhiErr, PsiErr if isPhi: dihedralDict[seqCode][0] = value dihedralDict[seqCode][2] = error else: dihedralDict[seqCode][1] = value dihedralDict[seqCode][3] = error phipsiData = [] seqCodes = dihedralDict.keys() seqCodes.sort() for seqCode in seqCodes: data = dihedralDict[seqCode] if None not in data: phi, psi, phiErr, psiErr = data phipsiData.append((seqCode, phi, psi, phiErr, psiErr)) # User options firstPPlaneFrag = self.firstResEntry.get() or 1 lastPPlaneFrag = self.lastResEntry.get() or 1 ppNbMin = self.numOptPlaneEntry.get() or 1 minValueBest = self.numOptHitsEntry.get() or 2 maxValueBest = self.maxOptStepEntry.get() or 5 strucData = Meccano.runFwd(firstPPlaneFrag, lastPPlaneFrag, ppNbMin, minValueBest, maxValueBest, RAMACHANDRAN_DATABASE, seqData, mediaData, phipsiData) if strucData: fileName = 'CcpnMeccanoPdb%f.pdb' % time.time() fileObj = open(fileName, 'w') ch = self.chain.pdbOneLetterCode.strip() if not ch: ch = self.chain.code[0].upper() i = 1 for atomType, resNb, x, y, z in strucData: resType = residueDict.get(resNb, '???') line = PDB_FORMAT % ('ATOM',i,'%-3s' % atomType,'',resType,ch,resNb,'',x,y,z,1.0,1.0) i += 1 fileObj.close() ensemble = getStructureFromFile(self.molSystem, fileName) if not self.toggleWritePdbFile.get(): os.unlink(fileName) self.run.outputEnsemble = ensemble self.run = None self.updateRuns() def getMediumName(self, alignMedium): self.mediumNameEntry.set(alignMedium.name) def getMediumDetails(self, alignMedium): self.mediumDetailsEntry.set(alignMedium.details) def setMediumName(self, event): value = self.mediumNameEntry.get() self.alignMedium.name = value or None def setMediumDetails(self, event): value = self.mediumDetailsEntry.get() self.alignMedium.details = value or None def setAlignMedium(self, alignMedium): if self.constraintSet: self.constraintSet.conditionState = alignMedium def getAlignMedium(self, constraintList): media = self.getAlignmentMedia() names = [am.name for am in media] if constraintList.conditionState in media: index = media.index(constraintList.conditionState) else: index = 0 self.alignMediumPulldown.setup(names, media, index) def toggleUseRestraints(self, constraintList): bool = constraintList.useForMeccano bool = not bool if bool and (not constraintList.conditionState) \ and (constraintList.className == 'RdcConsraintList'): msg = 'Cannot use RDC restraint list for Meccano ' msg += 'unless it is first associated with an amigment medium' showWarning('Warning', msg, parent=self) else: constraintList.useForMeccano = bool self.updateConstraintLists() def addStaticTensor(self): if self.alignMedium: tensor = Implementation.SymmTracelessMatrix(aAxial=0.0,aRhombic=0.0, alpha=0.0,beta=0.0, gamma=0.0) self.alignMedium.staticAlignment = tensor self.updateAlignMediaAfter(self.alignMedium) def addDynamicTensor(self): if self.alignMedium: tensor = Implementation.SymmTracelessMatrix(aAxial=0.0,aRhombic=0.0, alpha=0.0,beta=0.0, gamma=0.0) self.alignMedium.dynamicAlignment = tensor self.updateAlignMediaAfter(self.alignMedium) def removeTensor(self): if self.alignMedium and self.tensor: if self.tensor is self.alignMedium.dynamicAlignment: self.alignMedium.dynamicAlignment = None elif self.tensor is self.alignMedium.staticAlignment: self.alignMedium.staticAlignment = None self.updateAlignMediaAfter(self.alignMedium) def addAlignMedium(self): if self.constraintSet: medium = self.constraintSet.newConditionState() medium.name = 'Align Medium %d' % medium.serial def removeAlignMedium(self): if self.alignMedium: self.alignMedium.delete() def updateTensor(self, aAxial=None, aRhombic=None, alpha=None, beta=None, gamma=None): aAxial = aAxial or self.tensor.aAxial aRhombic = aRhombic or self.tensor.aRhombic alpha = alpha or self.tensor.alpha beta = beta or self.tensor.beta gamma = gamma or self.tensor.gamma tensor = Implementation.SymmTracelessMatrix(aAxial=aAxial, aRhombic=aRhombic, alpha=alpha,beta=beta, gamma=gamma) if self.alignMedium: if self.tensor is self.alignMedium.dynamicAlignment: self.alignMedium.dynamicAlignment = tensor elif self.tensor is self.alignMedium.staticAlignment: self.alignMedium.staticAlignment = tensor self.tensor = tensor def setAxial(self, event): value = self.editAxialEntry.get() self.updateTensor(aAxial=value) self.updateTensors() def setRhombic(self, event): value = self.editRhombicEntry.get() self.updateTensor(aRhombic=value) self.updateTensors() def setEulerAlpha(self, event): value = self.editAlphaEulerEntry.get() self.updateTensor(alpha=value) self.updateTensors() def setEulerBeta(self, event): value = self.editBetaEulerEntry.get() self.updateTensor(beta=value) self.updateTensors() def setEulerGamma(self, event): value = self.editGammaEulerEntry.get() self.updateTensor(gamma=value) self.updateTensors() def getAxial(self, tensor): value = tensor.aAxial self.editAxialEntry.set(value) def getRhombic(self, tensor): value = tensor.aRhombic self.editRhombicEntry.set(value) def getEulerAlpha(self, tensor): value = tensor.alpha self.editAlphaEulerEntry.set(value) def getEulerBeta(self, tensor): value = tensor.beta self.editBetaEulerEntry.set(value) def getEulerGamma(self, tensor): value = tensor.gamma self.editGammaEulerEntry.set(value) def selectTensor(self, tensor, row, col): self.tensor = tensor def selectAlignMedium(self, alignMedium, row, col): self.alignMedium = alignMedium self.updateTensors() def getAlignmentMedia(self): if self.constraintSet: return self.constraintSet.sortedConditionStates() else: return [] def updateAlignMedia(self): textMatrix = [] objectList = [] if self.constraintSet: objectList = self.getAlignmentMedia() for conditionState in objectList: staticTensor = None dyamicTensor = None tensor = conditionState.dynamicAlignment if tensor: vals = (tensor.aAxial, tensor.aRhombic, tensor.alpha, tensor.beta, tensor.gamma) dyamicTensor = u'\u03B6:%.3f \u03B7:%.3f \u03B1:%.3f \u03B2:%.3f \u03B3:%.3f ' % vals tensor = conditionState.staticAlignment if tensor: vals = (tensor.aAxial, tensor.aRhombic, tensor.alpha, tensor.beta, tensor.gamma) staticTensor = u'\u03B6:%.3f \u03B7:%.3f \u03B1:%.3f \u03B2:%.3f \u03B3:%.3f ' % vals datum = [conditionState.serial, conditionState.name, conditionState.details, dyamicTensor, staticTensor] textMatrix.append(datum) if dyamicTensor or staticTensor: if not self.alignMedium: self.alignMedium = conditionState self.mediaMatrix.update(textMatrix=textMatrix, objectList=objectList) if self.alignMedium: self.mediaMatrix.selectObject(self.alignMedium) def updateTensors(self): textMatrix = [] objectList = [] conditionState = self.alignMedium if conditionState: tensor = conditionState.dynamicAlignment if tensor: datum = ['Dynamic', tensor.aAxial, tensor.aRhombic, tensor.alpha, tensor.beta, tensor.gamma] textMatrix.append(datum) objectList.append(tensor) tensor = conditionState.staticAlignment if tensor: datum = ['Static', tensor.aAxial, tensor.aRhombic, tensor.alpha, tensor.beta, tensor.gamma] textMatrix.append(datum) objectList.append(tensor) self.tensorMatrix.update(textMatrix=textMatrix, objectList=objectList) def getMolSystems(self): molSystems = [] for molSystem in self.project.sortedMolSystems(): if molSystem.chains: molSystems.append(molSystem) return molSystems def updateMolSystems(self, *notifyObj): index = 0 names = [] molSystems = self.getMolSystems() molSystem = self.molSystem if molSystems: if molSystem not in molSystems: molSystem = molSystems[0] index = molSystems.index(molSystem) names = [ms.code for ms in molSystems] else: self.molSystem = None if self.molSystem is not molSystem: if self.run: self.run.molSystem = molSystem self.molSystem = molSystem self.updateChains() self.molSystemPulldown.setup(texts=names, objects=molSystems, index=index) def selectMolSystem(self, molSystem): if self.molSystem is not molSystem: if self.run: self.run.molSystem = molSystem self.molSystem = molSystem self.updateChains() def updateChains(self, *notifyObj): index = 0 names = [] chains = [] chain = self.chain if self.molSystem: chains = [c for c in self.molSystem.sortedChains() if c.residues] if chains: if chain not in chains: chain = chains[0] index = chains.index(chain) names = [c.code for c in chains] if chain is not self.chain: self.chain = chain self.updateResidueRanges() self.chainPulldown.setup(texts=names, objects=chains, index=index) def selectChain(self, chain): if chain is not self.chain: self.chain = chain self.updateResidueRanges() def updateResidueRanges(self): first = self.firstResEntry.get() last = self.lastResEntry.get() if self.chain: residues = self.chain.sortedResidues() firstSeq = residues[0].seqCode lastSeq = residues[-2].seqCode if first < firstSeq: first = firstSeq if last == first: last = lastSeq elif last > lastSeq: last = lastSeq if first > last: last, first = first, last self.firstResEntry.set(first) self.lastResEntry.set(last) return first, last def getConstraintSets(self): constraintSets = [] nmrProject = self.project.currentNmrProject for constraintSet in nmrProject.sortedNmrConstraintStores(): for constraintList in constraintSet.constraintLists: if constraintList.className not in ('ChemShiftConstraintList','somethingElse'): constraintSets.append(constraintSet) break return constraintSets def updateConstraintSets(self, *notifyObj): index = 0 names = [] constraintSets = self.getConstraintSets() constraintSet = self.constraintSet if constraintSets: if constraintSet not in constraintSets: constraintSet = constraintSets[0] index = constraintSets.index(constraintSet) names = ['%d' % cs.serial for cs in constraintSets] if constraintSet is not self.constraintSet: if self.run: self.run.inputConstraintStore = constraintSet self.constraintSet = constraintSet self.updateConstraintLists() self.constraintSetPulldown.setup(texts=names, objects=constraintSets, index=index) def selectConstraintSet(self, constraintSet): if self.constraintSet is not constraintSet: if self.run: self.run.inputConstraintStore = constraintSet self.constraintSet = constraintSet self.updateConstraintLists() def getConstraintLists(self): constraintLists = [] if self.constraintSet: for constraintList in self.constraintSet.sortedConstraintLists(): if constraintList.className not in ('ChemShiftConstraintList','somethingElse'): constraintLists.append(constraintList) return constraintLists def updateConstraintLists(self, *notifyObj): textMatrix = [] objectList = self.getConstraintLists() for constraintList in objectList: if not hasattr(constraintList, 'useForMeccano'): if constraintList.conditionState \ or (constraintList.className != 'RdcConstraintList'): bool = True else: bool = False constraintList.useForMeccano = bool if constraintList.conditionState: alignMedium = constraintList.conditionState.name else: alignMedium = None datum = [constraintList.serial, constraintList.className[:-14], constraintList.useForMeccano and 'Yes' or 'No', alignMedium, len(constraintList.constraints)] textMatrix.append(datum) self.restraintMatrix.update(textMatrix=textMatrix, objectList=objectList) def selectConstraint(self, obj, row, column): if self.constraint is not obj: self.constraint = obj def getSimStore(self): simStore = self.project.findFirstNmrSimStore(name='meccano') if not simStore: simStore = self.project.newNmrSimStore(name='meccano') return simStore def getRuns(self): runs = [None, ] if self.molSystem and self.constraintSet: simStore = self.getSimStore() runs += simStore.sortedRuns() return runs def updateRuns(self, *notifyObj): index = 0 names = ['<New>'] runs = self.getRuns() run = self.run if runs: if run not in runs: run = runs[0] index = runs.index(run) names += [r.serial for r in runs if r] if run is not self.run: self.updateConstraintSets() self.updateMolSystems() self.updateShiftLists() self.runPulldown.setup(names, runs, index) def updateRunParams(self, event=None): if self.run and self.molSystem and self.constraintSet: simRun = self.run simRun.inputConstraintStore = self.constraintSet simRun.molSystem = self.molSystem if self.shiftList: simRun.setInputMeasurementLists([self.shiftList,]) simRun.newRunParameter(code='FirstPepPlane',id=1, intValue=self.firstResEntry.get() or 0) simRun.newRunParameter(code='LastPepPlane' ,id=1, intValue=self.lastResEntry.get() or 0) simRun.newRunParameter(code='MaxOptSteps', id=1, intValue=self.maxOptStepEntry.get() or 0) simRun.newRunParameter(code='NumOptPlanes', id=1, intValue=self.numOptPlaneEntry.get() or 0) simRun.newRunParameter(code='MinOptHits', id=1, intValue=self.numOptHitsEntry.get() or 0) def makeSimRun(self, template=None): simStore = self.getSimStore() if template: molSystem = template.molSystem constraintSet = template.inputConstraintStore shiftList = template.findFirstInputMeasurementList(className='ShiftList') protocol = template.annealProtocol else: molSystem = self.molSystem constraintSet = self.constraintSet shiftList = self.shiftList protocol = self.annealProtocol simRun = simStore.newRun(annealProtocol=protocol, molSystem=molSystem, inputConstraintStore=protocol) if shiftList: simRun.addInputMeasurementList(shiftList) if template: for param in template.runParameters: simRun.newRunParameter(code=param.code, id=param.id, booleanValue=param.booleanValue, floatValue=param.floatValue, intValue=param.intValue, textValue=param.textValue) else: if self.chain: chainCode = self.chain.code else: chaincode = 'A' simRun.newRunParameter(code='FirstPepPlane',id=1, intValue=self.firstResEntry.get() or 0) simRun.newRunParameter(code='LastPepPlane' ,id=1, intValue=self.lastResEntry.get() or 0) simRun.newRunParameter(code='MaxOptSteps', id=1, intValue=self.maxOptStepEntry.get() or 0) simRun.newRunParameter(code='NumOptPlanes', id=1, intValue=self.numOptPlaneEntry.get() or 0) simRun.newRunParameter(code='MinOptHits', id=1, intValue=self.numOptHitsEntry.get() or 0) simRun.newRunParameter(code='ChainCode', id=1, textValue=chainCode) return simRun def selectRun(self, simRun): if self.run is not simRun: if simRun: if simRun.outputEnsemble: msg = 'Selected run has already been used to generate a structure.' msg += 'A new run will be setup based on the selection.' showWarning('Warning',msg) simRun = self.makeSimRun(template=simRun) molSystem = simRun.molSystem constraintSet = simRun.inputConstraintStore shiftList = simRun.findFirstInputMeasurementList(className='ShiftList') if molSystem and (self.molSystem is not molSystem): self.molSystem = molSystem self.updateMolSystems() if constraintSet and (self.constraintSet is not constraintSet): self.constraintSet = constraintSet self.updateConstraintSets() if shiftList and (self.shiftList is not shiftList): self.shiftList = shiftList self.updateShiftLists() firstPepPlane = simRun.findFirstrunParameter(code='FirstPepPlane') lastPepPlane = simRun.findFirstrunParameter(code='LastPepPlane') maxOptSteps = simRun.findFirstrunParameter(code='MaxOptSteps') numOptPlanes = simRun.findFirstrunParameter(code='NumOptPlanes') minOptHits = simRun.findFirstrunParameter(code='MinOptHits') chainCode = simRun.findFirstrunParameter(code='ChainCode') if firstPepPlane is not None: self.firstResEntry.set(firstPepPlane.intValue or 0) if lastPepPlane is not None: self.lastResEntry.set(lastPepPlane.intValue or 0) if maxOptSteps is not None: self.maxOptStepEntry.set(maxOptSteps.intValue or 0) if numOptPlanes is not None: self.numOptPlaneEntry.set(numOptPlanes.intValue or 0) if minOptHits is not None: self.numOptHitsEntry.set(minOptHits.intValue or 0) if chainCode is not None: chainCode = chainCode.textValue or 'A' if self.molSystem: chain = self.molSystem.findFirsChain(code=chainCode) if chain: self.selectChain(chain) self.run = simRun def updateShiftLists(self, *notifyObj): index = 0 names = [] nmrProject = self.project.currentNmrProject shiftLists = nmrProject.findAllMeasurementLists(className='ShiftList') shiftLists = [(sl.serial, sl) for sl in shiftLists] shiftLists.sort() shiftLists = [x[1] for x in shiftLists] shiftList = self.shiftList if shiftLists: if shiftList not in shiftLists: shiftList = shiftLists[0] index = shiftLists.index(shiftList) names = ['%d'% sl.serial for sl in shiftLists] if shiftList is not self.shiftList: if self.run: self.run.setInputMeasurementLists([shiftList]) self.shiftListPulldown.setup(texts=names, objects=shiftLists, index=index) def selectShiftList(self, shiftList): if shiftList is not self.shiftList: if self.run: self.run.setInputMeasurementLists([shiftList]) self.shiftList = shiftList
class NmrPipePseudoPopup(BasePopup): pseudoEntries = ('Is Pseudo Expt', 'Is Not Pseudo Expt') def __init__(self, parent, params, dim, fileName='', *args, **kw): self.dim = dim self.params = params self.fileName = fileName m = template_re.match(fileName) if m: n = len(m.groups(2)) ss = '%%0%dd' % n template = re.sub(template_re, r'\1%s\3' % ss, fileName) else: template = fileName self.template = template BasePopup.__init__(self, parent=parent, title='NMRPipe Pseudo Data', modal=True, **kw) def body(self, master): fileName = self.fileName directory = os.path.dirname(fileName) if not directory: directory = os.getcwd() fileName = os.path.basename(fileName) m = template_re.match(fileName) if m: n = len(m.groups(2)) ss = '%%0%dd' % n template = re.sub(template_re, r'\1%s\3' % ss, fileName) else: template = fileName master.rowconfigure(0, weight=1) master.rowconfigure(1, weight=1) master.columnconfigure(1, weight=1) tipTexts = [ 'The experiment is pseudo-N dimensional, with a sampled axis', 'The experiment is the regular kind with only NMR frequency axes' ] self.pseudoButton = RadioButtons( master, entries=self.pseudoEntries, select_callback=self.changedPseudoMode, grid=(0, 0), sticky='nw', tipTexts=tipTexts) frame = self.pseudoFrame = Frame(master) self.pseudoFrame.grid(row=1, column=0, sticky='nsew') row = 0 npts = self.params.npts[self.dim] tipText = 'Number of data points (planes) along sampled axis' label = Label(frame, text='Number of points: ') label.grid(row=row, column=0, sticky='e') self.nptsEntry = IntEntry(frame, text=npts, tipText=tipText, width=8, grid=(row, 1)) tipText = 'Load the values for the sampled axis from a text file containing a list of numeric values' Button(frame, text='Load values from text file', command=self.loadValues, tipText=tipText, grid=(row, 2), sticky='ew') row = row + 1 tipText = 'The values (e.g. T1, T2) corresponding to each data point (plane) along sampled axis' label = Label(frame, text='Point values: ') label.grid(row=row, column=0, sticky='e') self.valueEntry = FloatEntry(frame, isArray=True, tipText=tipText) self.valueEntry.grid(row=row, column=1, columnspan=2, sticky='ew') row = row + 1 tipText = 'Fetch the Point values from the files given by the NMRPipe template' button = Button( frame, text='Fetch values from file(s) specified by template below', command=self.fetchValues, tipText=tipText) button.grid(row=row, column=1, columnspan=2, sticky='w') row = row + 1 tipText = 'The directory where the data files reside' button = Button(frame, text='Data directory: ', command=self.chooseDirectory) button.grid(row=row, column=0, sticky='e') self.directoryEntry = Entry(frame, text=directory, width=40, tipText=tipText) self.directoryEntry.grid(row=row, column=1, columnspan=2, sticky='ew') row = row + 1 tipText = 'The NMRPipe template for the data files, if you want to use these to fetch the point values from' button = Button(frame, text='NMRPipe template: ', command=self.chooseFile) button.grid(row=row, column=0, sticky='e') self.templateEntry = Entry(frame, text=template, tipText=tipText) self.templateEntry.grid(row=row, column=1, columnspan=2, sticky='ew') for n in range(row): frame.rowconfigure(n, weight=1) frame.columnconfigure(1, weight=1) buttons = UtilityButtonList(master, closeText='Ok', doClone=False, closeCmd=self.updateParams, helpUrl=self.help_url) buttons.grid(row=2, column=0, sticky='ew') def loadValues(self): directory = self.parent.fileSelect.getDirectory() fileSelectPopup = FileSelectPopup(self, title='Select Sampled Data File', dismiss_text='Cancel', selected_file_must_exist=True, multiSelect=False, directory=directory) fileName = fileSelectPopup.file_select.getFile() if not fileName: return fileObj = open(fileName, 'rU') data = '' line = fileObj.readline() while line: data += line line = fileObj.readline() fileObj.close() data = re.sub(',\s+', ',', data) data = re.sub('\s+', ',', data) data = re.sub(',,', ',', data) data = re.sub('[^0-9,.\-+eE]', '', data) self.valueEntry.set(data) def chooseDirectory(self): directory = os.path.dirname(self.fileName) if not directory: directory = os.getcwd() popup = FileSelectPopup(self, directory=directory, show_file=False) directory = popup.getDirectory() popup.destroy() if directory: self.directoryEntry.set(directory) def chooseFile(self): directory = self.directoryEntry.get() if not directory: directory = os.getcwd() popup = FileSelectPopup(self, directory=directory) file = popup.getFile() popup.destroy() if file: template = os.path.basename(file) self.templateEntry.set(template) def updateParams(self): params = self.params if self.pseudoButton.get() == self.pseudoEntries[0]: npts = self.nptsEntry.get() params.npts[self.dim] = npts values = self.valueEntry.get() try: params.setSampledDim(self.dim, values) except ApiError, e: showError('Set Sampled Dim', e.error_msg, parent=self) return params.fixNullDims(ignoreDim=self.dim) else:
class EditPeakPopup(BasePopup): """ **Edit Position, Intensity & Details for a Peak** This popup window provides an means of editing peak information as an alternative to editing values in the main peak tables. This popup is also used to specify parameters for when a new peak is explicitly added to a peak list using a tabular display. The user can specify the position of the peak's dimensions in ppm, Hz or data point units. Also, the user can adjust the height and volume peak intensity values and a textual "Details" field, ,which can carry the user's comments about the peak. When editing an existing peak, no changes are made to the peak until the [Update] button is pressed. Likewise for a new peak the [Add Peak] button commits the changes. If the popup window is closed before the changes are committed then the entire editing or peak addition operation is cancelled. """ def __init__(self, parent, peak=None, peakList=None, *args, **kw): self.titleColor = '#000080' self.numDims = 0 self.peak = peak kw['borderwidth'] = 6 BasePopup.__init__(self, parent=parent, title='Edit Peak', **kw) self.registerNotify(self.deletedPeak, 'ccp.nmr.Nmr.Peak', 'delete') for func in ('setAnnotation', 'setDetails', 'setFigOfMerit'): self.registerNotify(self.updatePeak, 'ccp.nmr.Nmr.Peak', func) for func in ('setAnnotation', 'setPosition', 'setNumAliasing'): self.registerNotify(self.updatePeak, 'ccp.nmr.Nmr.PeakDim', func) for func in ('__init__', 'delete', 'setValue'): self.registerNotify(self.updatePeak, 'ccp.nmr.Nmr.PeakIntensity', func) self.dimensionLabels = [] self.dimensionEntries = [] self.update(self.peak, peakList) def body(self, guiParent): self.geometry("+150+150") guiParent.grid_columnconfigure(0, weight=1) self.master_frame = guiParent units = ('ppm', 'point', 'Hz') self.unit = 'ppm' self.specLabel = Label(guiParent, fg=self.titleColor, grid=(0, 0), sticky='ew') self.peakLabel = Label(guiParent, grid=(0, 1), sticky='ew') self.unit_frame = frame = Frame(guiParent, grid=(1, 1), gridSpan=(1, 2)) self.unitLabel = Label(frame, text='Current units: ', grid=(0, 0)) tipText = 'Selects which unit of measurement to display peak dimension positions with' self.unitSelect = PulldownList(frame, callback=self.changeUnit, texts=units, grid=(0, 1), tipText=tipText) self.heightLabel = Label(guiParent, text='Height', borderwidth=2, relief='groove') tipText = 'Sets the peak height; the value of the spectrum point intensity (albeit often interpolated)' self.heightEntry = FloatEntry(guiParent, borderwidth=1, tipText=tipText) self.volumeLabel = Label(guiParent, text='Volume', borderwidth=2, relief='groove') tipText = 'Sets the peak volume integral; normally a summation of data point values' self.volumeEntry = FloatEntry(guiParent, borderwidth=1, tipText=tipText) self.detailLabel = Label(guiParent, text='Details', borderwidth=2, relief='groove') tipText = 'A user-configurable textual comment for the peak, which appears an tables and occasionally on spectrum displays' self.detailEntry = Entry(guiParent, borderwidth=1, tipText=tipText) tipTexts = [ 'Commits the specified values to update the peak and closes the popup', ] texts = ['Update'] commands = [self.commit] self.buttons = UtilityButtonList(guiParent, texts=texts, commands=commands, doClone=False, helpUrl=self.help_url, tipTexts=tipTexts) def open(self): self.updatePeak() BasePopup.open(self) def updatePeak(self, object=None): peak = None if object: if object.className == 'Peak': peak = object elif object.className == 'PeakDim': peak = object.peak elif object.className == 'PeakIntensity': peak = object.peak if (peak is None) or (peak is self.peak): self.update(peak=self.peak) def update(self, peak=None, peakList=None): # first destroy old labels and entries (saves grid hassles) for label in self.dimensionLabels: label.destroy() for entry in self.dimensionEntries: entry.destroy() # now setup required data if peak: title = 'Edit Peak' self.buttons.buttons[0].config(text='Update') else: title = 'Add Peak' self.buttons.buttons[0].config(text='Add Peak') self.setTitle(title) self.peak = peak self.peakList = peakList if not peakList: if peak: self.peakList = peak.peakList else: return peakList = self.peakList spectrum = peakList.dataSource.name self.numDims = peakList.dataSource.numDim self.posn = self.numDims * [0] self.dataDims = peakList.dataSource.sortedDataDims() if self.peak: serial = self.peak.serial dims = self.peak.sortedPeakDims() details = self.peak.details if not details: details = '' if self.peak.annotation: annotn = '%0.16s' % self.peak.annotation else: annotn = '' heightIntensity = self.peak.findFirstPeakIntensity( intensityType='height') volumeIntensity = self.peak.findFirstPeakIntensity( intensityType='volume') if heightIntensity: height = heightIntensity.value else: height = 0.0 if volumeIntensity: volume = volumeIntensity.value else: volume = 0.0 for i in range(self.numDims): peakDim = dims[i] dataDimRef = peakDim.dataDimRef if dataDimRef: self.posn[i] = peakDim.position + ( peakDim.numAliasing * dataDimRef.dataDim.numPointsOrig) else: self.posn[i] = peakDim.position else: dict = peakList.__dict__.get('serialDict') if dict is None: serial = 1 else: serial = dict.get('peaks', 0) + 1 height = 0.0 volume = 0.0 details = '' annotn = '' self.specLabel.set( text='Experiment: %s Spectrum: %s PeakList: %d' % (peakList.dataSource.experiment.name, spectrum, peakList.serial)) self.peakLabel.set(text='Peak: %d' % serial) self.dimensionLabels = self.numDims * [''] self.dimensionEntries = self.numDims * [''] for i in range(self.numDims): pos = self.posn[i] if self.unit != 'point': dataDim = self.dataDims[i] if dataDim.className == 'FreqDataDim': pos = unit_converter[('point', self.unit)]( pos, getPrimaryDataDimRef(dataDim)) self.dimensionLabels[i] = Label(self.master_frame, text='F%d' % (i + 1), borderwidth=2, relief='groove') tipText = 'The peak position in dimension %d, in the specified units' % ( i + 1) self.dimensionEntries[i] = FloatEntry(self.master_frame, borderwidth=1, text='%8.4f' % pos, tipText=tipText) self.heightEntry.set(text='%f' % height) self.volumeEntry.set(text='%f' % volume) self.detailEntry.set(text=details) row = 0 self.specLabel.grid(row=row, column=0, columnspan=2, sticky='nsew') row = row + 1 self.peakLabel.grid(row=row, column=0, sticky='nsew') self.unit_frame.grid(row=row, column=1, columnspan=2, sticky='nsew') for i in range(self.numDims): row = row + 1 self.dimensionLabels[i].grid(row=row, column=0, sticky='nsew') self.dimensionEntries[i].grid(row=row, column=1, columnspan=3, sticky='e') row = row + 1 self.heightLabel.grid(row=row, column=0, sticky='nsew') self.heightEntry.grid(row=row, column=1, columnspan=3, sticky='e') row = row + 1 self.volumeLabel.grid(row=row, column=0, sticky='nsew') self.volumeEntry.grid(row=row, column=1, columnspan=3, sticky='e') row = row + 1 self.detailLabel.grid(row=row, column=0, sticky='nsew') self.detailEntry.grid(row=row, column=1, columnspan=3, sticky='e') row = row + 1 self.buttons.grid(row=row, column=0, columnspan=4, sticky='nsew') def changeUnit(self, unit): posDisp = self.numDims * [None] for i in range(self.numDims): posDisp[i] = float(self.dimensionEntries[i].get()) if self.unit != 'point': dataDim = self.dataDims[i] if dataDim.className == 'FreqDataDim': posDisp[i] = unit_converter[(self.unit, 'point')]( posDisp[i], getPrimaryDataDimRef(dataDim)) self.unit = unit if self.unit != 'point': for i in range(self.numDims): dataDim = self.dataDims[i] if dataDim.className == 'FreqDataDim': posDisp[i] = unit_converter[('point', self.unit)]( posDisp[i], getPrimaryDataDimRef(dataDim)) for i in range(self.numDims): value = posDisp[i] if value is None: self.dimensionEntries[i].set('None') else: self.dimensionEntries[i].set('%8.4f' % posDisp[i]) def commit(self): posDisp = self.numDims * [0] for i in range(self.numDims): posDisp[i] = float(self.dimensionEntries[i].get()) if self.unit != 'point': dataDim = self.dataDims[i] if dataDim.className == 'FreqDataDim': self.posn[i] = unit_converter[(self.unit, 'point')]( posDisp[i], getPrimaryDataDimRef(dataDim)) else: self.posn[i] = posDisp[i] if self.peak: movePeak(self.peak, self.posn) else: self.peak = pickPeak(self.peakList, self.posn) height = self.heightEntry.get() volume = self.volumeEntry.get() setManualPeakIntensity(self.peak, height, intensityType='height') setManualPeakIntensity(self.peak, volume, intensityType='volume') details = self.detailEntry.get() or None self.peak.setDetails(details) self.close() def deletedPeak(self, peak): if self.peak is peak: self.close() def destroy(self): self.unregisterNotify(self.deletedPeak, 'ccp.nmr.Nmr.Peak', 'delete') for func in ('setAnnotation', 'setDetails', 'setFigOfMerit'): self.unregisterNotify(self.updatePeak, 'ccp.nmr.Nmr.Peak', func) for func in ('setAnnotation', 'setPosition', 'setNumAliasing'): self.unregisterNotify(self.updatePeak, 'ccp.nmr.Nmr.PeakDim', func) for func in ('__init__', 'delete', 'setValue'): self.unregisterNotify(self.updatePeak, 'ccp.nmr.Nmr.PeakIntensity', func) BasePopup.destroy(self)
class AnnealingSettingsTab(object): '''This class describes the tab in the GUI where the user can change setting that govern the monte carlo / annleaing procedure. This also includes which information from the ccpn analysis project is used and which information is ignored. This includes: * present sequential assignments * tentative assignments * amino acid type information * whether to include untyped spin systems * assignments to peak dimensions ALso the chain can be selected here. Furthermore the user can set the temperature regime of the annealing, the amount of times the procedure is repeated to obtain statistics. The fraction of peaks that is left out in each run to diversify the results, the treshhold score for amino acid typing and the treshhold collabelling for a peak to be expected. ''' def __init__(self, parent, frame): '''Init. args: parent: the guiElement that this tab is part of. frame: the frame this part of the GUI lives in. ''' self.guiParent = parent self.frame = frame self.project = parent.project self.nmrProject = parent.nmrProject self.minIsoFrac = 0.1 self.leavePeaksOutFraction = 0.0 self.minTypeScore = 1.0 self.chain = None self.amountOfRepeats = 10 self.amountOfSteps = 10000 self.acceptanceConstantList = [0.0, 0.01, 0.015, 0.022, 0.033, 0.050, 0.075, 0.113, 0.170, 0.256, 0.384, 0.576, 0.864, 1.297, 1.946, 2.919, 4.378, 6.568, 9.852, 14.77, 22.16, 33.25] self.energyDataSets = [[]] self.residues = [] self.body() def body(self): '''describes the body of this tab. It bascically consists of some field to fill out for the user at the top and a ScrolledGraph that shows the progess of the annealing procedure a the bottom. ''' frame = self.frame # frame.expandGrid(13,0) frame.expandGrid(15, 1) row = 0 text = 'Calculate Assignment Suggestions' command = self.runCalculations self.startButton = Button(frame, command=command, text=text) self.startButton.grid(row=row, column=0, sticky='nsew', columnspan=2) row += 1 Label(frame, text='Amount of runs: ', grid=(row, 0)) tipText = 'The amount of times the whole optimization procedure is performed, each result is safed' self.repeatEntry = IntEntry(frame, grid=(row, 1), width=7, text=10, returnCallback=self.updateRepeatEntry, tipText=tipText, sticky='nsew') self.repeatEntry.bind('<Leave>', self.updateRepeatEntry, '+') row += 1 Label(frame, text='Temperature regime: ', grid=(row, 0)) tipText = 'This list of numbers govern the temperature steps during the annealing, every number represents 1/(kb*t), where kb is the Boltzmann constant and t the temperature of one step.' self.tempEntry = Entry(frame, text=map(str, self.acceptanceConstantList), width=64, grid=(row, 1), isArray=True, returnCallback=self.updateAcceptanceConstantList, tipText=tipText, sticky='nsew') row += 1 Label(frame, text='Amount of attempts per temperature:', grid=(row, 0)) tipText = 'The amount of attempts to switch the position of two spinsystems in the sequence are performed for each temperature point' self.NAStepEntry = IntEntry(frame, grid=(row, 1), width=7, text=10000, returnCallback=self.updateStepEntry, tipText=tipText, sticky='nsew') self.NAStepEntry.bind('<Leave>', self.updateStepEntry, '+') row += 1 Label(frame, text='Fraction of peaks to leave out:', grid=(row, 0)) tipText = 'In each run a fraction of the peaks can be left out of the optimization, thereby increasing the variability in the outcome and reducing false negatives. In each run this will be different randomly chosen sub-set of all peaks. 0.1 (10%) can be a good value.' self.leaveOutPeaksEntry = FloatEntry(frame, grid=(row, 1), width=7, text=0.0, returnCallback=self.updateLeavePeaksOutEntry, tipText=tipText, sticky='nsew') self.leaveOutPeaksEntry.bind( '<Leave>', self.updateLeavePeaksOutEntry, '+') row += 1 Label(frame, text='Minmal amino acid typing score:', grid=(row, 0)) tipText = 'If automatic amino acid typing is selected, a cut-off value has to set. Every amino acid type that scores higher than the cut-off is taken as a possible type. This is the same score as can be found under resonance --> spin systems --> predict type. Value should be between 0 and 100' self.minTypeScoreEntry = FloatEntry(frame, grid=(row, 1), width=7, text=1.0, returnCallback=self.updateMinTypeScoreEntry, tipText=tipText, sticky='nsew') self.minTypeScoreEntry.bind( '<Leave>', self.updateMinTypeScoreEntry, '+') row += 1 Label(frame, text='Minimal colabelling fraction:', grid=(row, 0)) tipText = 'The minimal amount of colabelling the different nuclei should have in order to still give rise to a peak.' self.minLabelEntry = FloatEntry(frame, grid=(row, 1), width=7, text=0.1, returnCallback=self.updateMinLabelEntry, tipText=tipText, sticky='nsew') self.minLabelEntry.bind('<Leave>', self.updateMinLabelEntry, '+') row += 1 Label(frame, text='Use sequential assignments:', grid=(row, 0)) tipText = 'When this option is select the present sequential assignments will be kept in place' self.useAssignmentsCheck = CheckButton( frame, selected=True, tipText=tipText, grid=(row, 1)) row += 1 Label(frame, text='Use tentative assignments:', grid=(row, 0)) tipText = 'If a spin system has tentative assignments this can be used to narrow down the amount of possible sequential assignments.' self.useTentativeCheck = CheckButton( frame, selected=True, tipText=tipText, grid=(row, 1)) row += 1 Label(frame, text='Use amino acid types:', grid=(row, 0)) tipText = 'Use amino acid types of the spin systems. If this option is not checked the spin systems are re-typed, only resonance names and frequencies are used' self.useTypeCheck = CheckButton( frame, selected=True, tipText=tipText, grid=(row, 1)) row += 1 Label(frame, text='Include untyped spin systems:', grid=(row, 0)) tipText = 'Also include spin system that have no type information. Amino acid typing will be done on the fly.' self.useAlsoUntypedSpinSystemsCheck = CheckButton( frame, selected=True, tipText=tipText, grid=(row, 1)) row += 1 Label(frame, text='Use dimensional assignments:', grid=(row, 0)) tipText = 'If one or more dimensions of a peak is already assigned, assume that this assignment is the only option. If not the check the program will consider all possibilities for the assignment of the dimension.' self.useDimensionalAssignmentsCheck = CheckButton( frame, selected=True, tipText=tipText, grid=(row, 1)) row += 1 Label(frame, text='Chain:', grid=(row, 0)) self.molPulldown = PulldownList( frame, callback=self.changeMolecule, grid=(row, 1)) self.updateChains() row += 1 Label(frame, text='Residue ranges: ', grid=(row, 0)) tipText = 'Which residues should be included. Example: "10-35, 62-100, 130".' self.residueRangeEntry = Entry(frame, text=None, width=64, grid=(row, 1), isArray=True, returnCallback=self.updateResidueRanges, tipText=tipText, sticky='nsew') self.updateResidueRanges(fromChain=True) row += 1 self.energyPlot = ScrolledGraph(frame, symbolSize=2, width=600, height=200, title='Annealing', xLabel='temperature step', yLabel='energy') self.energyPlot.grid(row=row, column=0, columnspan=2, sticky='nsew') def runCalculations(self): '''Run all calculations. Also triggers the disabling of some buttons and fields. ''' self.startButton.disable() self.disableIllegalButtonsAfterPrecalculations() self.guiParent.connector.runAllCalculations() self.startButton.configure(text='More runs') self.startButton.enable() def disableIllegalButtonsAfterPrecalculations(self): '''Disable buttons and field the user can not alter any longer after the model is set up and the 'pre-calculations' have finished. This is done because this part of the calculation should only be run once. All settings that would be changed after this point will not have any influence. ''' illegalButtons = [self.minTypeScoreEntry, self.minLabelEntry, self.useAlsoUntypedSpinSystemsCheck, self.useAssignmentsCheck, self.useTypeCheck, self.useDimensionalAssignmentsCheck, self.useTentativeCheck] for illegalButton in illegalButtons: illegalButton.configure(state='disabled') self.molPulldown.disable() def getChainName(self, chain): '''Get the name for a chain. args: chain: ccpn analysis chain object returns: chain name ''' return '%s:%s (%s)' % (chain.molSystem.code, chain.code, chain.molecule.molType) def getChains(self): '''Get all chains present in the project. returns: list of ccpn analysis chain objects ''' chains = [] if self.project: for molSystem in self.project.sortedMolSystems(): for chain in molSystem.sortedChains(): if chain.residues: chains.append(chain) return chains def updateChains(self, *opt): '''Updates the list of chains if a new one is added to or deleted from the project. Updates the pull down list where a chain can be selected. ''' index = 0 texts = [] chains = self.getChains() chain = self.chain if chains: if chain not in chains: chain = chains[0] texts = [self.getChainName(c) for c in chains] index = chains.index(chain) else: chain = None self.molPulldown.setup(texts, chains, index) if chain is not self.chain: self.chain = chain def changeMolecule(self, chain): '''Select a molecular chain.''' if chain is not self.chain: self.chain = chain self.updateResidueRanges(fromChain=True) def updateStepEntry(self, event=None): '''Update the value and entry that sets the amount of steps per temperature point. ''' value = self.NAStepEntry.get() if value == self.amountOfSteps: return if value < 1: self.NAStepEntry.set(1) self.amountOfSteps = 1 else: self.amountOfSteps = value self.NAStepEntry.set(value) def updateRepeatEntry(self, event=None): '''Update the value and entry of that sets the amount of times the whole annealing procedure is repeated in order to obtain statistics. ''' value = self.repeatEntry.get() if value == self.amountOfRepeats: return if value < 1: self.repeatEntry.set(1) self.amountOfRepeats = 1 else: self.amountOfRepeats = value self.repeatEntry.set(value) def updateMinTypeScoreEntry(self, event=None): '''Updates the value and the entry for the treshhold value for amino acid typing. ''' value = self.minTypeScoreEntry.get() if value == self.minTypeScore: return if value < 0: self.minTypeScoreEntry.set(0.0) self.minTypeScore = 0.0 elif value > 100: self.minTypeScoreEntry.set(100.0) self.minTypeScore = 100.0 else: self.minTypeScoreEntry.set(value) self.minTypeScore = value def updateMinLabelEntry(self, event=None): '''Updates the minimum colabelling fraction for which a peak is expected to be present in the spectra. ''' value = self.minLabelEntry.get() if value == self.minIsoFrac: return if value < 0: self.minIsoFrac = 0.0 self.minLabelEntry.set(0.0) elif value > 1: self.minIsoFrac = 1.0 self.minLabelEntry.set(1.0) else: self.minIsoFrac = value self.minLabelEntry.set(value) def updateLeavePeaksOutEntry(self, event=None): '''Updates the value and entry of the fraction of peaks that should be left out in each run in order to diversify the results. ''' value = self.leaveOutPeaksEntry.get() if value == self.leavePeaksOutFraction: return if value < 0: self.leavePeaksOutFraction = 0.0 self.leaveOutPeaksEntry.set(0.0) elif value > 1: self.leavePeaksOutFraction = 1.0 self.leaveOutPeaksEntry.set(1.0) else: self.leavePeaksOutFraction = value self.leaveOutPeaksEntry.set(value) def updateAcceptanceConstantList(self, event=None): '''Updates the list with constants that are used during the monte carlo procedure to decide whether a changed is accepted or not. ''' acList = self.tempEntry.get() newList = [] for constant in acList: try: number = float(constant) newList.append(number) except ValueError: string = constant + \ ' in temperature constants is not a number.' showWarning('Not A Number', string, parent=self.guiParent) return False self.acceptanceConstantList = newList return True def updateResidueRanges(self, event=None, fromChain=False): self.residues = set() subRanges = self.residueRangeEntry.get() if not subRanges or fromChain: self.residues = set(self.chain.residues) residues = self.chain.sortedResidues() text = '{}-{}'.format(residues[0].seqCode, residues[-1].seqCode) self.residueRangeEntry.set(text=text) return for subRange in subRanges: indeces = subRange.split('-') start = int(indeces[0]) stop = int(indeces[-1]) + 1 for seqCode in range(start, stop): residue = self.chain.findFirstResidue(seqCode=seqCode) if not residue: showWarning('Residue out of range.', 'There is no residue at position {}'.format(seqCode), parent=self.guiParent) self.residues = set() return self.residues.add(residue) def addEnergyPoint(self, energy, time): '''Adds a point to the graph that shows the progress of the annealling procedure. args: energy: the y-value time: the x-value ''' point = (time, energy * -1) # This means one run has finished if len(self.energyDataSets[-1]) / (len(self.acceptanceConstantList) + 1): self.energyDataSets.append([point]) else: self.energyDataSets[-1].append(point) colors = colorSeries Ncolors = len(colors) NdataSets = len(self.energyDataSets) colorList = (NdataSets / Ncolors) * colors + \ colors[:NdataSets % Ncolors] self.energyPlot.update(dataSets=self.energyDataSets, dataColors=colorList) # Forcing the graph to draw, eventhough calculations # are still running. Only do this with high numbers of # steps, otherwise drawing takes longer than annealling. if self.amountOfSteps >= 100000: self.energyPlot.draw()
class CingFrame(NmrCalcRunFrame): def __init__(self, parent, application, *args, **kw): project = application.project self.nmrProject = nmrProject = application.nmrProject if project: calcStore = project.findFirstNmrCalcStore(name=APP_NAME, nmrProject=nmrProject) or \ project.newNmrCalcStore(name=APP_NAME, nmrProject=nmrProject) else: calcStore = None self.application = application self.residue = None self.structure = None self.serverCredentials = None self.iCingBaseUrl = DEFAULT_URL self.resultsUrl = None self.chain = None self.serverDone = False NmrCalcRunFrame.__init__(self, parent, project, calcStore, *args, **kw) # # # # # # New Structure Frame # # # # # self.structureTable.grid_forget() self.structureButtons.grid_forget() self.ensemblePulldown.grid_forget() self.modelButtons.grid_forget() self.modelPulldown.grid_forget() frame = self.inputTabs.frames[0] frame.grid_rowconfigure(0, weight=0) frame.grid_rowconfigure(1, weight=1) label = Label(frame, text='Ensemble: ', grid=(0, 0)) self.structurePulldown = PulldownList( frame, callback=self.changeStructure, grid=(0, 1), tipText='The structure ensemble coordinates to submit') tipTexts = [ 'Conformational model number', 'Whether analyse this model' ] headingList = ['Model', 'Use'] editWidgets = [None, None] editGetCallbacks = [None, self.toggleModel] editSetCallbacks = [ None, None, ] self.modelTable = ScrolledMatrix(frame, grid=(1, 0), gridSpan=(1, 2), callback=self.selectStructModel, multiSelect=True, tipTexts=tipTexts, editWidgets=editWidgets, initialRows=2, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, headingList=headingList) tipTexts = [ 'Activate the selected models so that they will be consedered in the analysis', 'Deactivate the selected models so that they will not be considered in the analysis' ] texts = ['Activate Selected', 'Inactivate Selected'] commands = [self.activateModels, self.disableModels] buttons = ButtonList(frame, texts=texts, commands=commands, grid=(2, 0), gridSpan=(1, 2), tipTexts=tipTexts) # # # # # # Submission frame # # # # # # tab = self.tabbedFrame.frames[1] tab.expandGrid(1, 0) frame = LabelFrame(tab, text='Server Job Submission', grid=(0, 0)) frame.expandGrid(None, 2) srow = 0 label = Label(frame, text='iCing URL:', grid=(srow, 0)) self.iCingBaseUrlPulldown = PulldownList( frame, texts=URLS, objects=URLS, index=0, grid=(srow, 1), tipText='Web location of iCING server to use') srow += 1 label = Label(frame, text='Results File:', grid=(srow, 0)) self.resultFileEntry = Entry( frame, bd=1, text='', grid=(srow, 1), width=50, tipText='Name of file to store compressed CING results in') self.setZipFileName() button = Button(frame, text='Choose File', bd=1, sticky='ew', command=self.chooseZipFile, grid=(srow, 2), tipText='Select file to overwrite with CING results') srow += 1 label = Label(frame, text='Results URL:', grid=(srow, 0)) self.resultUrlEntry = Entry( frame, bd=1, text='', grid=(srow, 1), width=50, tipText='Web location where CING results will be posted') button = Button(frame, text='View Results HTML', bd=1, sticky='ew', command=self.viewHtmlResults, grid=(srow, 2), tipText='Open the HTML CING results in a web browser') srow += 1 tipTexts = [ 'Submit the CCPN project to the CING server', 'Determin whether the iCING job is complete, pending or has failed', 'Remove all trace of the last submissionfrom the iCING server', 'Download the compressed CING results, including HTML' ] texts = [ 'Submit Project!', 'Check Run Status', 'Purge Server Result', 'Download Results' ] commands = [ self.runCingServer, self.checkStatus, self.purgeCingServer, self.downloadResults ] self.buttonBar = ButtonList(frame, texts=texts, commands=commands, grid=(srow, 0), gridSpan=(1, 3), tipTexts=tipTexts) for button in self.buttonBar.buttons[:1]: button.config(bg=CING_BLUE) # # # # # # Residue frame # # # # # # frame = LabelFrame(tab, text='Residue Options', grid=(1, 0)) frame.expandGrid(1, 1) label = Label(frame, text='Chain: ') label.grid(row=0, column=0, sticky='w') self.chainPulldown = PulldownList( frame, callback=self.changeChain, tipText='Select the molecular system chain to consider') self.chainPulldown.grid(row=0, column=1, sticky='w') headingList = ['#', 'Residue', 'Linking', 'Decriptor', 'Use?'] tipTexts = [ 'Sequence number', 'Residue type code', 'In-chain connectivity of residue', 'Protonation and steriochemical state', 'Whether to consider the residue in the analysis' ] editWidgets = [None, None, None, None, None] editGetCallbacks = [None, None, None, None, self.toggleResidue] editSetCallbacks = [ None, None, None, None, None, ] self.residueMatrix = ScrolledMatrix(frame, headingList=headingList, multiSelect=True, tipTexts=tipTexts, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, callback=self.selectResidue) self.residueMatrix.grid(row=1, column=0, columnspan=2, sticky='nsew') tipTexts = [ 'Use the selected residues in the analysis', 'Do not use the selected residues in the analysis' ] texts = ['Activate Selected', 'Inactivate Selected'] commands = [self.activateResidues, self.deactivateResidues] self.resButtons = ButtonList(frame, texts=texts, commands=commands, tipTexts=tipTexts) self.resButtons.grid(row=2, column=0, columnspan=2, sticky='ew') """ # # # # # # Validate frame # # # # # # frame = LabelFrame(tab, text='Validation Options', grid=(2,0)) frame.expandGrid(None,2) srow = 0 self.selectCheckAssign = CheckButton(frame) self.selectCheckAssign.grid(row=srow, column=0,sticky='nw' ) self.selectCheckAssign.set(True) label = Label(frame, text='Assignments and shifts') label.grid(row=srow,column=1,sticky='nw') srow += 1 self.selectCheckResraint = CheckButton(frame) self.selectCheckResraint.grid(row=srow, column=0,sticky='nw' ) self.selectCheckResraint.set(True) label = Label(frame, text='Restraints') label.grid(row=srow,column=1,sticky='nw') srow += 1 self.selectCheckQueen = CheckButton(frame) self.selectCheckQueen.grid(row=srow, column=0,sticky='nw' ) self.selectCheckQueen.set(False) label = Label(frame, text='QUEEN') label.grid(row=srow,column=1,sticky='nw') srow += 1 self.selectCheckScript = CheckButton(frame) self.selectCheckScript.grid(row=srow, column=0,sticky='nw' ) self.selectCheckScript.set(False) label = Label(frame, text='User Python script\n(overriding option)') label.grid(row=srow,column=1,sticky='nw') self.validScriptEntry = Entry(frame, bd=1, text='') self.validScriptEntry.grid(row=srow,column=2,sticky='ew') scriptButton = Button(frame, bd=1, command=self.chooseValidScript, text='Browse') scriptButton.grid(row=srow,column=3,sticky='ew') """ # # # # # # # # # # self.update(calcStore) self.administerNotifiers(application.registerNotify) def downloadResults(self): if not self.run: msg = 'No current iCing run' showWarning('Failure', msg, parent=self) return credentials = self.serverCredentials if not credentials: msg = 'No current iCing server job' showWarning('Failure', msg, parent=self) return fileName = self.resultFileEntry.get() if not fileName: msg = 'No save file specified' showWarning('Failure', msg, parent=self) return if os.path.exists(fileName): msg = 'File %s already exists. Overwite?' % fileName if not showOkCancel('Query', msg, parent=self): return url = self.iCingBaseUrl iCingUrl = self.getServerUrl(url) logText = iCingRobot.iCingFetch(credentials, url, iCingUrl, fileName) print logText msg = 'Results saved to file %s\n' % fileName msg += 'Purge results from iCing server?' if showYesNo('Query', msg, parent=self): self.purgeCingServer() def getServerUrl(self, baseUrl): iCingUrl = os.path.join(baseUrl, 'icing/icing/serv/iCingServlet') return iCingUrl def viewHtmlResults(self): resultsUrl = self.resultsUrl if not resultsUrl: msg = 'No current iCing results URL' showWarning('Failure', msg, parent=self) return webBrowser = WebBrowser(self.application, popup=self.application) webBrowser.open(self.resultsUrl) def runCingServer(self): if not self.project: return run = self.run if not run: msg = 'No CING run setup' showWarning('Failure', msg, parent=self) return structureData = self.getStructureData() if not structureData: msg = 'No structure ensemble selected' showWarning('Failure', msg, parent=self) return if not structureData.models: msg = 'No structural models selected from ensemble' showWarning('Failure', msg, parent=self) return residueData = self.getResidueData() if not (residueData and residueData.residues): msg = 'No active residues selected in structure' showWarning('Failure', msg, parent=self) return url = self.iCingBaseUrlPulldown.getObject() url.strip() if not url: msg = 'No iCing server URL specified' showWarning('Failure', msg, parent=self) self.iCingBaseUrl = None return msg = 'Submit job now? You will be informed when the job is done.' if not showOkCancel('Confirm', msg, parent=self): return self.run.status = 'pending' self.iCingBaseUrl = url iCingUrl = self.getServerUrl(url) self.serverCredentials, results, tarFileName = iCingRobot.iCingSetup( self.project, userId='ccpnAp', url=iCingUrl) if not results: # Message already issued on failure self.run.status = 'failed' self.serverCredentials = None self.resultsUrl = None self.update() return else: credentials = self.serverCredentials os.unlink(tarFileName) entryId = iCingRobot.iCingProjectName(credentials, iCingUrl).get( iCingRobot.RESPONSE_RESULT) baseUrl, htmlUrl, logUrl, zipUrl = iCingRobot.getResultUrls( credentials, entryId, url) self.resultsUrl = htmlUrl # Save server data in this run for persistence setRunParameter(run, iCingRobot.FORM_USER_ID, self.serverCredentials[0][1]) setRunParameter(run, iCingRobot.FORM_ACCESS_KEY, self.serverCredentials[1][1]) setRunParameter(run, ICING_BASE_URL, url) setRunParameter(run, HTML_RESULTS_URL, htmlUrl) self.update() #run.inputStructures = structure.sortedModels() # select residues from the structure's chain #iCingRobot.iCingResidueSelection(credentials, iCingUrl, residueText) # Select models from ensemble #iCingRobot.iCingEnsembleSelection(credentials, iCingUrl, ensembleText) # Start the actual run self.serverDone = False iCingRobot.iCingRun(credentials, iCingUrl) # Fetch server progress occasionally, report when done # this function will call itself again and again self.after(CHECK_INTERVAL, self.timedCheckStatus) self.update() def timedCheckStatus(self): if not self.serverCredentials: return if self.serverDone: return status = iCingRobot.iCingStatus(self.serverCredentials, self.getServerUrl(self.iCingBaseUrl)) if not status: #something broke, already warned self.run.status = 'failed' return result = status.get(iCingRobot.RESPONSE_RESULT) if result == iCingRobot.RESPONSE_DONE: self.serverDone = True self.run.status = 'completed' msg = 'CING run is complete!' showInfo('Completion', msg, parent=self) return self.after(CHECK_INTERVAL, self.timedCheckStatus) def checkStatus(self): if not self.serverCredentials: return status = iCingRobot.iCingStatus(self.serverCredentials, self.getServerUrl(self.iCingBaseUrl)) if not status: #something broke, already warned return result = status.get(iCingRobot.RESPONSE_RESULT) if result == iCingRobot.RESPONSE_DONE: msg = 'CING run is complete!' showInfo('Completion', msg, parent=self) self.serverDone = True return else: msg = 'CING job is not done.' showInfo('Processing', msg, parent=self) self.serverDone = False return def purgeCingServer(self): if not self.project: return if not self.run: msg = 'No CING run setup' showWarning('Failure', msg, parent=self) return if not self.serverCredentials: msg = 'No current iCing server job' showWarning('Failure', msg, parent=self) return url = self.iCingBaseUrl results = iCingRobot.iCingPurge(self.serverCredentials, self.getServerUrl(url)) if results: showInfo('Info', 'iCing server results cleared') self.serverCredentials = None self.iCingBaseUrl = None self.serverDone = False deleteRunParameter(self.run, iCingRobot.FORM_USER_ID) deleteRunParameter(self.run, iCingRobot.FORM_ACCESS_KEY) deleteRunParameter(self.run, HTML_RESULTS_URL) else: showInfo('Info', 'Purge failed') self.update() def chooseZipFile(self): fileTypes = [ FileType('Zip', ['*.zip']), ] popup = FileSelectPopup(self, file_types=fileTypes, file=self.resultFileEntry.get(), title='Results zip file location', dismiss_text='Cancel', selected_file_must_exist=False) fileName = popup.getFile() if fileName: self.resultFileEntry.set(fileName) popup.destroy() def setZipFileName(self): if self.project: zipFile = '%s_CING_report.zip' % self.project.name self.resultFileEntry.set(zipFile) else: self.resultFileEntry.set('CING_report.zip') def selectStructModel(self, model, row, col): self.model = model def selectResidue(self, residue, row, col): self.residue = residue def getResidueData(self): chain = self.chain.chain chainCode = chain.code msCode = chain.molSystem.code dataObj = self.run.findFirstData(className=RESIDUE_DATA, ioRole='input', molSystemCode=msCode, chainCode=chainCode, name=APP_NAME) if not dataObj: dataObj = self.run.newMolResidueData(molSystemCode=msCode, chainCode=chainCode, ioRole='input', name=APP_NAME) # if not dataObj.residueSeqIds: seqIds = [r.seqId for r in self.chain.sortedResidues()] dataObj.residueSeqIds = seqIds return dataObj def getStructureData(self): eId = self.structure.ensembleId msCode = self.structure.molSystem.code dataObj = self.run.findFirstData(className=STRUCTURE_DATA, molSystemCode=msCode, ensembleId=eId, ioRole='input', name=APP_NAME) if not dataObj: dataObj = self.run.newStructureEnsembleData(ioRole='input', molSystemCode=msCode, ensembleId=eId, name=APP_NAME) # if not dataObj.modelSerials: serials = [m.serial for m in self.structure.sortedModels()] dataObj.modelSerials = serials for dataObjB in self.run.findAllData(className=STRUCTURE_DATA, name=APP_NAME, ioRole='input'): if dataObjB is not dataObj: dataObjB.delete() return dataObj def deactivateResidues(self): if self.run and self.chain: dataObj = self.getResidueData() seqIds = set(dataObj.residueSeqIds) for residue in self.residueMatrix.currentObjects: seqId = residue.seqId if seqId in seqIds: seqIds.remove(seqId) seqIds = list(seqIds) seqIds.sort() dataObj.residueSeqIds = seqIds self.updateResidues() def activateResidues(self): if self.run and self.chain: for residue in self.residueMatrix.currentObjects: dataObj = self.getResidueData() seqIds = set(dataObj.residueSeqIds) for residue in self.residueMatrix.currentObjects: seqId = residue.seqId if seqId not in seqIds: seqIds.add(seqId) seqIds = list(seqIds) seqIds.sort() dataObj.residueSeqIds = seqIds self.updateResidues() def activateModels(self): if self.run and self.structure: dataObj = self.getStructureData() serials = set(dataObj.modelSerials) for model in self.modelTable.currentObjects: serial = model.serial if serial not in serials: serials.add(serial) serials = list(serials) serials.sort() dataObj.modelSerials = serials self.updateModels() def disableModels(self): if self.run and self.structure: dataObj = self.getStructureData() serials = set(dataObj.modelSerials) for model in self.modelTable.currentObjects: serial = model.serial if serial in serials: serials.remove(serial) serials = list(serials) serials.sort() dataObj.modelSerials = serials self.updateModels() def toggleModel(self, *opt): if self.model and self.run and self.structure: dataObj = self.getStructureData() serials = set(dataObj.modelSerials) serial = self.model.serial if serial in serials: serials.remove(serial) else: serials.add(serial) serials = list(serials) serials.sort() dataObj.modelSerials = serials self.updateModels() def toggleResidue(self, *opt): if self.residue and self.run: dataObj = self.getResidueData() seqIds = set(dataObj.residueSeqIds) seqId = self.residue.seqId if seqId in seqIds: seqIds.remove(seqId) else: seqIds.add(seqId) seqIds = list(seqIds) seqIds.sort() dataObj.residueSeqIds = seqIds self.updateResidues() def updateResidues(self): if self.residue and (self.residue.topObject is not self.structure): self.residue = None textMatrix = [] objectList = [] colorMatrix = [] if self.chain: resDataObj = self.getResidueData() selectedRes = set(resDataObj.residues) chainCode = self.chain.code for residue in self.chain.sortedResidues(): msResidue = residue.residue if msResidue in selectedRes: colors = [None, None, None, None, CING_BLUE] use = 'Yes' else: colors = [None, None, None, None, None] use = 'No' datum = [ residue.seqCode, msResidue.ccpCode, msResidue.linking, msResidue.descriptor, use, ] textMatrix.append(datum) objectList.append(residue) colorMatrix.append(colors) self.residueMatrix.update(objectList=objectList, textMatrix=textMatrix, colorMatrix=colorMatrix) def updateChains(self): index = 0 names = [] chains = [] chain = self.chain if self.structure: chains = self.structure.sortedCoordChains() names = [chain.code for chain in chains] if chains: if chain not in chains: chain = chains[0] index = chains.index(chain) self.changeChain(chain) self.chainPulldown.setup(names, chains, index) def updateStructures(self): index = 0 names = [] structures = [] structure = self.structure if self.run: structures0 = [(s.ensembleId, s) for s in self.project.structureEnsembles] structures0.sort() for eId, structure0 in structures0: name = '%s:%s' % (structure0.molSystem.code, eId) structures.append(structure0) names.append(name) if structures: if structure not in structures: structure = structures[-1] index = structures.index(structure) if self.structure is not structure: self.changeStructure(structure) self.structurePulldown.setup(names, structures, index) def updateModels(self, obj=None): textMatrix = [] objectList = [] colorMatrix = [] if self.structure and self.run: strucDataObj = self.getStructureData() selectedSerials = set(strucDataObj.modelSerials) for model in self.structure.sortedModels(): if model.serial in selectedSerials: colors = [None, CING_BLUE] use = 'Yes' else: colors = [None, None] use = 'No' datum = [model.serial, use] textMatrix.append(datum) objectList.append(model) colorMatrix.append(colors) self.modelTable.update(objectList=objectList, textMatrix=textMatrix, colorMatrix=colorMatrix) def changeStructure(self, structure): if self.project and (self.structure is not structure): self.project.currentEstructureEnsemble = structure self.structure = structure if self.run: dataObj = self.getStructureData() serials = set(dataObj.modelSerials) serials2 = set([m.serial for m in structure.models]) serials = list(serials & serials2) serials.sort() dataObj.modelSerials = serials dataObj.ensembleId = structure.ensembleId dataObj.molSystemCode = structure.molSystem.code # Could clean up residue data if required # prob OK to have haning around in case structure # changes back #for dataObj in self.run.findAllData(className=RESIDUE_DATA, # ioRole='input', # molSystemCode=msCode, # name=APP_NAME) # dataObj.delete() self.updateModels() self.updateChains() def changeChain(self, chain): if self.project and (self.chain is not chain): self.chain = chain self.updateResidues() #def chooseValidScript(self): # # # Prepend default Cyana file extension below # fileTypes = [ FileType('Python', ['*.py']), ] # popup = FileSelectPopup(self, file_types = fileTypes, # title='Python file', dismiss_text='Cancel', # selected_file_must_exist = True) # fileName = popup.getFile() # self.validScriptEntry.set(fileName) # popup.destroy() def updateAll(self, project=None): if project: self.project = project self.nmrProject = nmrProject = project.currentNmrProject calcStore = project.findFirstNmrCalcStore(name='CING', nmrProject=nmrProject) or \ project.newNmrCalcStore(name='CING', nmrProject=nmrProject) else: calcStore = None if not self.project: return self.setZipFileName() if not self.project.currentNmrProject: name = self.project.name self.nmrProject = self.project.newNmrProject(name=name) else: self.nmrProject = self.project.currentNmrProject self.update(calcStore) def update(self, calcStore=None): NmrCalcRunFrame.update(self, calcStore) run = self.run urls = URLS index = 0 if run: userId = getRunTextParameter(run, iCingRobot.FORM_USER_ID) accessKey = getRunTextParameter(run, iCingRobot.FORM_ACCESS_KEY) if userId and accessKey: self.serverCredentials = [(iCingRobot.FORM_USER_ID, userId), (iCingRobot.FORM_ACCESS_KEY, accessKey)] url = getRunTextParameter(run, ICING_BASE_URL) if url: htmlUrl = getRunTextParameter(run, HTML_RESULTS_URL) self.iCingBaseUrl = url self.resultsUrl = htmlUrl # May be None self.resultUrlEntry.set(self.resultsUrl) if self.iCingBaseUrl and self.iCingBaseUrl not in urls: index = len(urls) urls.append(self.iCingBaseUrl) self.iCingBaseUrlPulldown.setup(urls, urls, index) self.updateButtons() self.updateStructures() self.updateModels() self.updateChains() def updateButtons(self, event=None): buttons = self.buttonBar.buttons if self.project and self.run: buttons[0].enable() if self.resultsUrl and self.serverCredentials: buttons[1].enable() buttons[2].enable() buttons[3].enable() else: buttons[1].disable() buttons[2].disable() buttons[3].disable() else: buttons[0].disable() buttons[1].disable() buttons[2].disable() buttons[3].disable()
class PrintFrame(LabelFrame): def __init__(self, parent, getOption=None, setOption=None, text='Print Options', haveTicks=False, doOutlineBox=True, *args, **kw): self.getOption = getOption self.setOption = setOption self.haveTicks = haveTicks self.doOutlineBox = doOutlineBox LabelFrame.__init__(self, parent=parent, text=text, *args, **kw) self.file_select_popup = None self.getOptionValues() try: size_index = self.paper_types.index(self.paper_type) except: size_index = 0 try: other_unit_index = self.paper_units.index(self.other_unit) except: other_unit_index = 0 try: orientation_index = self.paper_orientations.index(self.orientation) except: orientation_index = 0 try: style_index = self.style_choices.index(self.output_style) except: style_index = 0 try: format_index = self.format_choices.index(self.output_format) except: format_index = 0 if haveTicks: try: tick_location_index = self.tick_locations.index( self.tick_location) except: tick_location_index = 0 self.grid_columnconfigure(2, weight=1) row = 0 label = Label(self, text='File:') label.grid(row=row, column=0, sticky='e') self.file_entry = Entry(self, width=40, text=self.file_name) self.file_entry.grid(row=row, column=1, columnspan=2, sticky='ew') button = Button(self, text='Choose File', command=self.findFile) button.grid(row=row, column=3, rowspan=2, sticky='nsew') row += 1 label = Label(self, text='Title:') label.grid(row=row, column=0, sticky='e') self.title_entry = Entry(self, width=40, text=self.title) self.title_entry.grid(row=row, column=1, columnspan=2, sticky='ew') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(4, weight=1) label = Label(frame, text='Paper size:') label.grid(row=0, column=0, sticky='e') entries = [] for t in paper_types: if t == Output.other_paper_type: entry = t else: (w, h, u) = paper_sizes[t] entry = t + ' (%2.1f %s x %2.1f %s)' % (w, u, h, u) entries.append(entry) self.size_menu = PulldownList(frame, callback=self.changedSize, texts=entries, index=size_index) self.size_menu.grid(row=0, column=1, sticky='w') self.other_frame = Frame(frame) self.other_frame.grid_columnconfigure(0, weight=1) self.other_entry = FloatEntry(self.other_frame, text=self.other_size, isArray=True) self.other_entry.grid(row=0, column=0, sticky='ew') self.other_unit_menu = PulldownList(self.other_frame, texts=paper_units, index=other_unit_index) self.other_unit_menu.grid(row=0, column=1, sticky='ew') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) frame.grid_columnconfigure(3, weight=1) frame.grid_columnconfigure(5, weight=1) label = Label(frame, text='Orientation:') label.grid(row=0, column=0, sticky='e') self.orientation_menu = PulldownList(frame, texts=paper_orientations, index=orientation_index) self.orientation_menu.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Style:') label.grid(row=0, column=2, sticky='e') self.style_menu = PulldownList(frame, texts=style_choices, index=style_index) self.style_menu.grid(row=0, column=3, sticky='w') label = Label(frame, text=' Format:') label.grid(row=0, column=4, sticky='e') self.format_menu = PulldownList(frame, callback=self.changedFormat, texts=format_choices, index=format_index) self.format_menu.grid(row=0, column=5, sticky='w') if haveTicks: row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) frame.grid_columnconfigure(3, weight=1) label = Label(frame, text='Tick Location:') label.grid(row=0, column=0, sticky='e') self.tick_menu = PulldownList(frame, texts=tick_locations, index=tick_location_index) self.tick_menu.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Tick Placement:') label.grid(row=0, column=2, sticky='e') self.tick_buttons = CheckButtons(frame, entries=tick_placements, selected=self.tick_placement) self.tick_buttons.grid(row=0, column=3, sticky='w') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(3, weight=1) label = Label(frame, text='Include:') label.grid(row=0, column=0, sticky='e') self.border_buttons = CheckButtons(frame, entries=border_decorations, selected=self.border_decoration) self.border_buttons.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Scaling:') label.grid(row=0, column=2, sticky='e') self.scaling_scale = Scale(frame, orient=Tkinter.HORIZONTAL, value=self.scaling) self.scaling_scale.grid(row=0, column=3, sticky='ew') def destroy(self): self.setOptionValues() if self.file_select_popup: self.file_select_popup.destroy() Frame.destroy(self) def getOptionValues(self): getOption = self.getOption if getOption: file_name = getOption('FileName', defaultValue='') title = getOption('Title', defaultValue='') paper_type = getOption('PaperSize', defaultValue=paper_types[0]) paper_type = paper_type_dict.get(paper_type, paper_types[0]) other_height = getOption('OtherHeight', defaultValue=10) other_width = getOption('OtherWidth', defaultValue=10) other_size = [other_height, other_width] other_unit = getOption('OtherUnit', defaultValue=paper_units[0]) orientation = getOption('Orientation', defaultValue=paper_orientations[0]) in_color = getOption('InColor', defaultValue=True) if in_color: output_style = style_choices[0] else: output_style = style_choices[1] format_option = getOption('OutputFormat', defaultValue=format_options[0]) output_format = format_choices[format_options.index(format_option)] if self.haveTicks: tick_outside = getOption('TickOutside', defaultValue=tick_locations[0]) if tick_outside: tick_location = tick_locations.index(PrintTicks.Outside) else: tick_location = tick_locations.index(PrintTicks.Inside) tick_placement = getTickPlacement1( getOption('TickPlacement', defaultValue='nsew')) dateTime = getOption('ShowsDateTime', defaultValue=True) fileName = getOption('ShowsFileName', defaultValue=True) border_decoration = [] if dateTime: border_decoration.append(border_decorations[0]) if fileName: border_decoration.append(border_decorations[1]) scaling = getOption('Scaling', defaultValue=0.9) scaling = int(round(100.0 * scaling)) else: file_name = '' title = '' paper_type = paper_types[0] other_unit = paper_units[0] other_size = '' orientation = paper_orientations[0] output_style = style_choices[0] output_format = format_choices[0] if self.haveTicks: tick_location = tick_locations[0] tick_placement = tick_placements border_decoration = border_decorations scaling = 90 if not self.haveTicks: tick_location = None tick_placement = None self.file_name = file_name self.title = title self.paper_type = paper_type self.other_unit = other_unit self.other_size = other_size self.orientation = orientation self.output_style = output_style self.output_format = output_format self.tick_location = tick_location self.tick_placement = tick_placement self.border_decoration = border_decoration self.scaling = scaling def setOptionValues(self): self.file_name = file_name = self.file_entry.get() self.title = title = self.title_entry.get() n = self.size_menu.getSelectedIndex() self.paper_type = paper_type = paper_types[n] if paper_type == Output.other_paper_type: other_size = self.other_entry.get() other_unit = self.other_unit_menu.getText() else: other_size = None other_unit = None self.other_size = other_size self.other_unit = other_unit self.paper_orientation = paper_orientation = self.orientation_menu.getText( ) self.output_style = output_style = self.style_menu.getText() self.output_format = output_format = self.format_menu.getText() if self.haveTicks: tick_location = self.tick_menu.getText() tick_placement = self.tick_buttons.getSelected() else: tick_location = tick_placement = None self.tick_location = tick_location self.tick_placement = tick_placement self.border_decoration = border_decoration = self.border_buttons.getSelected( ) scaling = self.scaling_scale.get() self.scaling = scaling = int(round(scaling)) setOption = self.setOption if setOption: setOption('FileName', value=file_name) setOption('Title', value=title) if paper_type == Output.other_paper_type: setOption('OtherHeight', value=other_size[0]) setOption('OtherWidth', value=other_size[1]) setOption('OtherUnit', value=other_unit) else: paper_type = paper_type_inverse_dict[paper_type] setOption('PaperSize', value=paper_type) setOption('Orientation', value=paper_orientation) in_color = (output_style == style_choices[0]) setOption('InColor', value=in_color) output_format = format_options[format_choices.index(output_format)] setOption('OutputFormat', value=output_format) if self.haveTicks: tick_outside = (tick_location == PrintTicks.Outside) setOption('TickOutside', value=tick_outside) tick_placement = getTickPlacement2(tick_placement) setOption('TickPlacement', value=tick_placement) dateTime = (border_decorations[0] in border_decoration) fileName = (border_decorations[1] in border_decoration) setOption('ShowsDateTime', value=dateTime) setOption('ShowsFileName', value=fileName) setOption('Scaling', value=0.01 * scaling) def findFile(self): if self.file_select_popup: self.file_select_popup.open() else: file_types = [ FileType('All', ['*']), FileType('PostScript', ['*.ps', '*.eps']), FileType('PDF', ['*.pdf', '*.ai']) ] self.file_select_popup = FileSelectPopup(self, file_types=file_types) file = self.file_select_popup.getFile() if file: self.file_entry.set(file) def changedSize(self, entry): if entry == Output.other_paper_type: self.other_frame.grid(row=0, column=2, columnspan=2, sticky='w') else: self.other_frame.grid_forget() def changedFormat(self, entry): file_suffix = file_suffixes.get(entry) if not file_suffix: return file_name = self.file_entry.get() if not file_name: return for suffix in format_suffixes: if file_name.endswith(suffix): if suffix != file_suffix: n = len(suffix) file_name = file_name[:-n] + file_suffix self.file_entry.set(file_name) break else: file_name = file_name + file_suffix self.file_entry.set(file_name) # width and height are of plot, in pixels def getOutputHandler(self, width, height, fonts=None): if not fonts: fonts = [] else: fonts = list(fonts) for n in range(len(fonts)): if fonts[n] == 'Times': fonts[n] = 'Times-Roman' self.setOptionValues() if not self.file_name: showError('No file', 'No file specified', parent=self) return None if os.path.exists(self.file_name): if not showYesNo('File exists', 'File "%s" exists, overwrite?' % self.file_name, parent=self): return None if (self.paper_type == Output.other_paper_type): paper_size = self.other_size + [self.other_unit] else: paper_size = paper_sizes[self.paper_type] output_scaling = self.scaling / 100.0 font = 'Times-Roman' border_text = {} for decoration in self.border_decoration: if (decoration == 'Time & date'): location = 'se' text = time.ctime(time.time()) elif (decoration == 'File name'): location = 'sw' text = self.file_name else: continue # should not be here border_text[location] = (text, font, 12) if (self.title): location = 'n' border_text[location] = (self.title, font, 18) if font not in fonts: fonts.append(font) outputHandler = PrintHandler.getOutputHandler( self.file_name, width, height, output_scaling=output_scaling, paper_size=paper_size, paper_orientation=self.paper_orientation, output_style=self.output_style, output_format=self.output_format, border_text=border_text, fonts=fonts, do_outline_box=self.doOutlineBox) return outputHandler def getAspectRatio(self): self.setOptionValues() if self.paper_type == Output.other_paper_type: paper_size = self.other_size else: paper_size = paper_sizes[self.paper_type] r = paper_size[1] / paper_size[0] if self.paper_orientation == 'Landscape': r = 1.0 / r return r
class EntryScale(Frame): def __init__(self, parent, values, initial_index=0, orient=Tkinter.HORIZONTAL, scale_length=200, entry_width=5, entry_format='%2.1f', set_callback=None, *args, **kw): assert len(values) > 0 self.entry_format = entry_format self.values = tuple(values) self.set_callback = set_callback apply(Frame.__init__, (self, parent) + args, kw) if (orient == Tkinter.HORIZONTAL): self.grid_columnconfigure(1, weight=1) sticky = Tkinter.EW row = 0 col = 1 else: self.grid_rowconfigure(1, weight=1) sticky = Tkinter.NS row = 1 col = 0 self.entry = Entry(self, width=entry_width) self.entry.grid(row=0, column=0, sticky=sticky) self.entry.bind('<KeyPress>', self.keyPress) self.scale = Tkinter.Scale(self, orient=orient, length=scale_length, from_=0, to=len(values) - 1, label='', showvalue=0, command=self.setCallback) self.scale.grid(row=row, column=col, sticky=sticky) self.set(initial_index) def get(self): return self.scale.get() def getValue(self): return self.values[self.get()] def set(self, index): assert index >= 0 and index < len( self.values), 'index = %s, len(self.values) = %s' % ( index, len(self.values)) self.scale.set(index) self.setEntry() def setEntry(self): index = self.get() text = self.entry_format % self.values[index] self.entry.set(text) def setCallback(self, ignore): self.setEntry() if (self.set_callback): self.set_callback(self.get()) def keyPress(self, event): if (event.keysym == 'Return'): text = self.entry.get() try: value = text.atof() except: return min_index = 0 min_diff = abs(self.values[0] - value) for i in range(1, len(self.values)): diff = abs(self.values[i] - value) if (diff < min_diff): min_index = i min_diff = diff self.set(min_index)
class PrintFrame(Frame): def __init__(self, parent, getOption = None, setOption = None, haveTicks = False, doOutlineBox = True, *args, **kw): self.getOption = getOption self.setOption = setOption self.haveTicks = haveTicks self.doOutlineBox = doOutlineBox Frame.__init__(self, parent=parent, *args, **kw) self.file_select_popup = None self.getOptionValues() try: size_index = paper_types.index(self.paper_type) except: size_index = 0 try: other_unit_index = paper_units.index(self.other_unit) except: other_unit_index = 0 try: orientation_index = paper_orientations.index(self.paper_orientation) except: orientation_index = 0 try: style_index = style_choices.index(self.output_style) except: style_index = 0 try: format_index = format_choices.index(self.output_format) except: format_index = 0 if haveTicks: try: tick_location_index = tick_locations.index(self.tick_location) except: tick_location_index = 0 self.grid_columnconfigure(1, weight=1) row = 0 button = Button(self, text='File:', command=self.findFile, tipText='Select location to save print file') button.grid(row=row, column=0, sticky='e') self.file_entry = Entry(self, width=40, text=self.file_name, tipText='Location where file is saved on disk') self.file_entry.grid(row=row, column=1, sticky='ew') row += 1 label = Label(self, text='Title:') label.grid(row=row, column=0, sticky='e') self.title_entry = Entry(self, width=40, text=self.title, tipText='Title of the printout, displayed at top') self.title_entry.grid(row=row, column=1, sticky='ew') row += 1 label = Label(self, text='X axis label:') label.grid(row=row, column=0, sticky='e') self.x_axis_entry = Entry(self, width=40, text=self.x_axis_label, tipText='X axis label for the printout') self.x_axis_entry.grid(row=row, column=1, sticky='ew') row += 1 label = Label(self, text='Y axis label:') label.grid(row=row, column=0, sticky='e') self.y_axis_entry = Entry(self, width=40, text=self.y_axis_label, tipText='Y axis label for the printout') self.y_axis_entry.grid(row=row, column=1, sticky='ew') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=2, sticky='ew') frame.grid_columnconfigure(4, weight=1) label = Label(frame, text='Paper size:') label.grid(row=0, column=0, sticky='e') entries = [] for t in paper_types: if t == Output.other_paper_type: entry = t else: (w, h, u) = paper_sizes[t] entry = t + ' (%2.1f %s x %2.1f %s)' % (w, u, h, u) entries.append(entry) self.size_menu = PulldownList(frame, callback=self.changedSize, texts=entries, index=size_index, tipText='The paper size for the printout') self.size_menu.grid(row=0, column=1, sticky='w') self.other_frame = Frame(frame) self.other_frame.grid_columnconfigure(0, weight=1) self.other_entry = FloatEntry(self.other_frame, text=self.other_size, isArray=True, tipText='The size of the Other paper in both dimensions; this requires two values, space or comma separated') self.other_entry.grid(row=0, column=0, sticky='ew') self.other_unit_menu= PulldownList(self.other_frame, texts=paper_units, index=other_unit_index, tipText='The unit for the Other paper size') self.other_unit_menu.grid(row=0, column=1, sticky='ew') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) frame.grid_columnconfigure(3, weight=1) frame.grid_columnconfigure(5, weight=1) label = Label(frame, text='Orientation:') label.grid(row=0, column=0, sticky='e') self.orientation_menu = PulldownList(frame, texts=paper_orientations, index=orientation_index, tipText='Whether the paper should be set in Portrait or Landscape mode') self.orientation_menu.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Style:') label.grid(row=0, column=2, sticky='e') self.style_menu = PulldownList(frame, texts=style_choices, index=style_index, tipText='Whether the printout should be in colour or black and white') self.style_menu.grid(row=0, column=3, sticky='w') label = Label(frame, text=' Format:') label.grid(row=0, column=4, sticky='e') self.format_menu = PulldownList(frame, callback=self.changedFormat, texts=format_choices, index=format_index, tipText='Whether to save as PS, EPS or PDF') self.format_menu.grid(row=0, column=5, sticky='w') if haveTicks: row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) frame.grid_columnconfigure(3, weight=1) label = Label(frame, text='Tick Location:') label.grid(row=0, column=0, sticky='e') self.tick_menu = PulldownList(frame, texts=tick_locations, index=tick_location_index, tipText='Whether the tick marks appear on the inside or outside of the frame') self.tick_menu.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Tick Placement:') label.grid(row=0, column=2, sticky='e') if self.tick_placement is None: selected = None else: selected = [(x in self.tick_placement) for x in tick_placements] self.tick_buttons = CheckButtons(frame, entries=tick_placements, selected=selected, tipTexts=('Whether the tick marks appear on the top and/or bottom and/or left and/or right',)) self.tick_buttons.grid(row=0, column=3, sticky='w') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) frame.grid_columnconfigure(3, weight=1) label = Label(frame, text='Tick Font:') label.grid(row=0, column=0, sticky='e') self.tick_font_list = FontList(frame, mode='Print', selected=self.tick_font, extraTexts=[PrintTicks.no_tick_text], tipText='The font used for the tick mark labels') self.tick_font_list.grid(row=0, column=1, sticky='w') label = Label(frame, text='Tick Spacing:') label.grid(row=0, column=2, sticky='e') # TBD: put preferred choice in data model self.spacing_menu = PulldownList(frame, texts=spacing_choices, index=0, callback=self.changedSpacing, tipText='Whether the program should automatically calculate the major/minor tick spacings and how many decimal places are used for the ticks, or whether the these are specified manually') self.spacing_menu.grid(row=0, column=3, sticky='w') ff = self.spacing_frame = Frame(frame) ff.grid_columnconfigure(1, weight=1) ff.grid_columnconfigure(2, weight=1) label = Label(ff, text='Tick Spacing') label.grid(row=0, column=0, sticky='w') label = Label(ff, text='Major') label.grid(row=0, column=1, sticky='ew') label = Label(ff, text='Minor') label.grid(row=0, column=2, sticky='ew') label = Label(ff, text='Decimals') label.grid(row=0, column=3, sticky='ew') label = Label(ff, text='X:') label.grid(row=1, column=0, sticky='w') self.x_major_entry = FloatEntry(ff, tipText='The spacing in display units of the major tick marks in the X dimension') self.x_major_entry.grid(row=1, column=1, sticky='ew') self.x_minor_entry = FloatEntry(ff, tipText='The spacing in display units of the minor tick marks in the X dimension (not printed if left blank)') self.x_minor_entry.grid(row=1, column=2, sticky='ew') self.x_decimal_entry = IntEntry(ff, tipText='The number of decimal places for the tick numbers in the X dimension') self.x_decimal_entry.grid(row=1, column=3, sticky='ew') label = Label(ff, text='Y:') label.grid(row=2, column=0, sticky='w') self.y_major_entry = FloatEntry(ff, tipText='The spacing in display units of the major tick marks in the Y dimension') self.y_major_entry.grid(row=2, column=1, sticky='ew') self.y_minor_entry = FloatEntry(ff, tipText='The spacing in display units of the minor tick marks in the Y dimension (not printed if left blank)') self.y_minor_entry.grid(row=2, column=2, sticky='ew') self.y_decimal_entry = IntEntry(ff, tipText='The number of decimal places for the tick numbers in the Y dimension') self.y_decimal_entry.grid(row=2, column=3, sticky='ew') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(1, weight=1) label = Label(frame, text='Tick Length:') label.grid(row=0, column=0, sticky='e') # TBD: put preferred choice in data model self.tick_length_menu = PulldownList(frame, texts=tick_length_choices, index=0, callback=self.changedLength, tipText='Whether the program should automatically calculate the major/minor tick lengths, or whether the these are specified manually') self.tick_length_menu.grid(row=0, column=1, sticky='w') ff = self.length_frame = Frame(frame) ff.grid_columnconfigure(1, weight=1) label = Label(ff, text=' Major length:') label.grid(row=0, column=0, sticky='w') self.length_major_entry = FloatEntry(ff, tipText='The length in points of the major tick marks') self.length_major_entry.grid(row=0, column=1, sticky='w') label = Label(ff, text='Minor length:') label.grid(row=0, column=2, sticky='w') self.length_minor_entry = FloatEntry(ff, tipText='The length in points of the minor tick marks') self.length_minor_entry.grid(row=0, column=3, sticky='w') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='ew') frame.grid_columnconfigure(3, weight=1) frame.grid_columnconfigure(4, weight=1) label = Label(frame, text='Scaling:') label.grid(row=0, column=0, sticky='e') # TBD: put preferred choice in data model self.scaling_menu = PulldownList(frame, texts=scaling_choices, index=0, callback=self.changedScaling, tipText='Whether the plot should be scaled as a percentage of the maximum size that would fit on the paper, or instead should be specified by the number of cms or inches per unit') self.scaling_menu.grid(row=0, column=1, sticky='ew') self.scaling_scale = Scale(frame, orient=Tkinter.HORIZONTAL, value=self.scaling, tipText='The percentage of the maximum size that would fit on the paper that the plot is scaled by') self.scaling_scale.grid(row=0, column=2, columnspan=3, sticky='ew') self.x_scaling_label = Label(frame, text='X:') self.x_scaling_entry = FloatEntry(frame, tipText='The scaling that should be used in the X dimension as cms or inches per unit') self.y_scaling_label = Label(frame, text='Y:') self.y_scaling_entry = FloatEntry(frame, tipText='The scaling that should be used in the Y dimension as cms or inches per unit') row += 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=4, sticky='w') frame.grid_columnconfigure(2, weight=1) label = Label(frame, text='Include:') label.grid(row=0, column=0, sticky='e') tipTexts = ('Whether the time and date should be included in the printout', 'Whether the file name should be included in the printout') if self.border_decoration is None: selected = None else: selected = [(x in self.border_decoration) for x in border_decorations] self.border_buttons = CheckButtons(frame, entries=border_decorations, selected=selected, tipTexts=tipTexts) self.border_buttons.grid(row=0, column=1, sticky='w') label = Label(frame, text=' Using Font:') label.grid(row=0, column=2, sticky='e') self.border_font_list = FontList(frame, mode='Print', selected=self.border_font, tipText='The font used for the border texts') self.border_font_list.grid(row=0, column=3, sticky='w') row += 1 label = Label(self, text='Line width:') label.grid(row=row, column=0, sticky='w') self.linewidth_entry = FloatEntry(self, width=10, text=self.linewidth, tipText='Line width for drawing') self.linewidth_entry.grid(row=row, column=1, sticky='w') def destroy(self): self.setOptionValues() if self.file_select_popup: self.file_select_popup.destroy() Frame.destroy(self) def getOptionValues(self): getOption = self.getOption if getOption: file_name = getOption('FileName', defaultValue='') title = getOption('Title', defaultValue='') x_axis_label = getOption('XAxisLabel', defaultValue='') y_axis_label = getOption('YAxisLabel', defaultValue='') paper_type = getOption('PaperSize', defaultValue=paper_types[0]) paper_type = paper_type_dict.get(paper_type, paper_types[0]) other_height = getOption('OtherHeight', defaultValue=10) other_width = getOption('OtherWidth', defaultValue=10) other_size = [other_height, other_width] other_unit = getOption('OtherUnit', defaultValue=paper_units[0]) paper_orientation = getOption('Orientation', defaultValue=paper_orientations[0]) in_color = getOption('InColor', defaultValue=True) if in_color: output_style = style_choices[0] else: output_style = style_choices[1] format_option = getOption('OutputFormat', defaultValue=format_options[0]) output_format = format_choices[format_options.index(format_option)] if self.haveTicks: tick_outside = getOption('TickOutside', defaultValue=tick_locations[0]) if tick_outside: tick_location = tick_locations.index(PrintTicks.Outside) else: tick_location = tick_locations.index(PrintTicks.Inside) tick_placement = getTickPlacement1(getOption('TickPlacement', defaultValue='nsew')) dateTime = getOption('ShowsDateTime', defaultValue=True) fileName = getOption('ShowsFileName', defaultValue=True) border_font = getOption('BorderFont', defaultValue='Helvetica 10') border_decoration = [] if dateTime: border_decoration.append(border_decorations[0]) if fileName: border_decoration.append(border_decorations[1]) if self.haveTicks: spacing_choice = getOption('SpacingChoice', defaultValue=spacing_choices[0]) x_major = getOption('XMajor', defaultValue=1.0) x_minor = getOption('XMinor', defaultValue=1.0) x_decimal = getOption('XDecimal', defaultValue=3) y_major = getOption('YMajor', defaultValue=1.0) y_minor = getOption('YMinor', defaultValue=1.0) y_decimal = getOption('YDecimal', defaultValue=3) tick_length_choice = getOption('TickLengthChoice', defaultValue=tick_length_choices[0]) tick_major = getOption('TickMajor', defaultValue=10) tick_minor = getOption('TickMinor', defaultValue=5) scaling_choice = getOption('ScalingChoice', defaultValue=scaling_choices[0]) scaling = getOption('Scaling', defaultValue=0.7) scaling = int(round(100.0 * scaling)) x_scaling = getOption('XScaling', defaultValue=1.0) y_scaling = getOption('YScaling', defaultValue=1.0) if self.haveTicks: tick_font = getOption('TickFont', defaultValue='Helvetica 10') linewidth = getOption('LineWidth', defaultValue=Output.default_linewidth) else: file_name = '' title = '' x_axis_label = '' y_axis_label = '' paper_type = paper_types[0] other_unit = paper_units[0] other_size = '' paper_orientation = paper_orientations[0] output_style = style_choices[0] output_format = format_choices[0] if self.haveTicks: tick_location = tick_locations[0] tick_placement = tick_placements border_decoration = border_decorations border_font = 'Helvetica 10' if self.haveTicks: spacing_choice = spacing_choices[0] x_major = 1.0 x_minor = 1.0 x_decimal = 3 y_major = 1.0 y_minor = 1.0 y_decimal = 3 tick_length_choice = tick_length_choices[0] tick_major = 10 tick_minor = 5 scaling_choice = scaling_choices[0] scaling = 70 x_scaling = 1.0 y_scaling = 1.0 if self.haveTicks: tick_font = 'Helvetica 10' linewidth = Output.default_linewidth if not self.haveTicks: tick_location = None tick_placement = None spacing_choice = spacing_choices[0] x_major = 1.0 x_minor = 1.0 x_decimal = 3 y_major = 1.0 y_minor = 1.0 y_decimal = 3 tick_font = 'Helvetica 10' tick_length_choice = tick_length_choices[0] tick_major = 10 tick_minor = 5 self.file_name = file_name self.title = title self.x_axis_label = x_axis_label self.y_axis_label = y_axis_label self.paper_type = paper_type self.other_unit = other_unit self.other_size = other_size self.paper_orientation = paper_orientation self.output_style = output_style self.output_format = output_format self.tick_location = tick_location self.tick_placement = tick_placement self.border_decoration = border_decoration self.border_font = border_font self.spacing_choice = spacing_choice self.x_major = x_major self.x_minor = x_minor self.x_decimal = x_decimal self.y_major = y_major self.y_minor = y_minor self.y_decimal = y_decimal self.scaling_choice = scaling_choices[0] self.scaling = scaling self.x_scaling = x_scaling self.y_scaling = y_scaling self.tick_font = tick_font self.linewidth = linewidth self.tick_length_choice = tick_length_choice self.tick_major = tick_major self.tick_minor = tick_minor def setOptionValues(self): if not hasattr(self, 'file_entry'): # it looks like on destroy can have function called but file_entry deleted already return self.file_name = file_name = self.file_entry.get() self.title = title = self.title_entry.get() self.x_axis_label = x_axis_label = self.x_axis_entry.get() self.y_axis_label = y_axis_label = self.y_axis_entry.get() n = self.size_menu.getSelectedIndex() self.paper_type = paper_type = paper_types[n] if paper_type == Output.other_paper_type: other_size = self.other_entry.get() other_unit = self.other_unit_menu.getText() else: other_size = None other_unit = None self.other_size = other_size self.other_unit = other_unit self.paper_orientation = paper_orientation = self.orientation_menu.getText() self.output_style = output_style = self.style_menu.getText() self.output_format = output_format = self.format_menu.getText() if self.haveTicks: tick_location = self.tick_menu.getText() tick_placement = self.tick_buttons.getSelected() else: tick_location = tick_placement = None self.tick_location = tick_location self.tick_placement = tick_placement self.border_decoration = border_decoration = self.border_buttons.getSelected() self.border_font = border_font = self.border_font_list.getText() if self.haveTicks: self.spacing_choice = spacing_choice = self.spacing_menu.getText() if spacing_choice != spacing_choices[0]: self.x_major = self.x_major_entry.get() self.x_minor = self.x_minor_entry.get() self.x_decimal = self.x_decimal_entry.get() self.y_major = self.y_major_entry.get() self.y_minor = self.y_minor_entry.get() self.y_decimal = self.y_decimal_entry.get() self.tick_length_choice = tick_length_choice = self.tick_length_menu.getText() if tick_length_choice != tick_length_choices[0]: self.tick_major = self.length_major_entry.get() self.tick_minor = self.length_minor_entry.get() self.scaling_choice = scaling_choice = self.scaling_menu.getText() if self.scaling_choice == scaling_choices[0]: scaling = self.scaling_scale.get() self.scaling = int(round(scaling)) else: self.x_scaling = self.x_scaling_entry.get() self.y_scaling = self.y_scaling_entry.get() if self.haveTicks: self.tick_font = self.tick_font_list.getText() self.linewidth = self.linewidth_entry.get() setOption = self.setOption if setOption: setOption('FileName', value=file_name) setOption('Title', value=title) setOption('XAxisLabel', value=x_axis_label) setOption('YAxisLabel', value=y_axis_label) if paper_type == Output.other_paper_type: setOption('OtherHeight', value=other_size[0]) setOption('OtherWidth', value=other_size[1]) setOption('OtherUnit', value=other_unit) else: paper_type = paper_type_inverse_dict[paper_type] setOption('PaperSize', value=paper_type) setOption('Orientation', value=paper_orientation) in_color = (output_style == style_choices[0]) setOption('InColor', value=in_color) output_format = format_options[format_choices.index(output_format)] setOption('OutputFormat', value=output_format) if self.haveTicks: tick_outside = (tick_location == PrintTicks.Outside) setOption('TickOutside', value=tick_outside) tick_placement = getTickPlacement2(tick_placement) setOption('TickPlacement', value=tick_placement) dateTime = (border_decorations[0] in border_decoration) fileName = (border_decorations[1] in border_decoration) setOption('ShowsDateTime', value=dateTime) setOption('ShowsFileName', value=fileName) setOption('BorderFont', value=border_font) if self.haveTicks: setOption('SpacingChoice', value=spacing_choice) if spacing_choice != spacing_choices[0]: setOption('XMajor', self.x_major) setOption('XMinor', self.x_minor) setOption('XDecimal', self.x_decimal) setOption('YMajor', self.y_major) setOption('YMinor', self.y_minor) setOption('YDecimal', self.y_decimal) setOption('TickLengthChoice', value=tick_length_choice) if tick_length_choice != tick_length_choices[0]: setOption('TickMajor', self.tick_major) setOption('TickMinor', self.tick_minor) setOption('ScalingChoice', value=scaling_choice) if scaling_choice == scaling_choices[0]: setOption('Scaling', value=0.01*self.scaling) else: setOption('XScaling', value=self.x_scaling) setOption('YScaling', value=self.y_scaling) if self.haveTicks: setOption('TickFont', self.tick_font) setOption('LineWidth', self.linewidth) def findFile(self): if self.file_select_popup: self.file_select_popup.open() else: file_types = [ FileType('All', ['*']), FileType('PostScript', ['*.ps', '*.eps']), FileType('PDF', ['*.pdf', '*.ai']) ] self.file_select_popup = FileSelectPopup(self, file_types=file_types) file = self.file_select_popup.getFile() if file: self.file_entry.set(file) def changedSize(self, entry): if entry == Output.other_paper_type: self.other_frame.grid(row=0, column=2, columnspan=2, sticky='w') else: self.other_frame.grid_forget() def changedFormat(self, entry): file_suffix = file_suffixes.get(entry) if not file_suffix: return file_name = self.file_entry.get() if not file_name: return for suffix in format_suffixes: if file_name.endswith(suffix): if suffix != file_suffix: n = len(suffix) file_name = file_name[:-n] + file_suffix self.file_entry.set(file_name) break else: file_name = file_name + file_suffix self.file_entry.set(file_name) def changedScaling(self, choice): if choice == scaling_choices[0]: self.scaling_scale.grid(row=0, column=2, columnspan=3, sticky='ew') self.x_scaling_label.grid_forget() self.x_scaling_entry.grid_forget() self.y_scaling_label.grid_forget() self.y_scaling_entry.grid_forget() else: self.scaling_scale.grid_forget() self.x_scaling_label.grid(row=0, column=2, sticky='w') self.x_scaling_entry.grid(row=0, column=3, columnspan=2, sticky='ew') self.y_scaling_label.grid(row=1, column=2, sticky='w') self.y_scaling_entry.grid(row=1, column=3, columnspan=2, sticky='ew') self.setOptionValues() def changedSpacing(self, choice): if choice == spacing_choices[0]: self.spacing_frame.grid_forget() else: self.spacing_frame.grid(row=1, column=1, columnspan=3, sticky='ew') self.setOptionValues() def changedLength(self, choice): if choice == tick_length_choices[0]: self.length_frame.grid_forget() else: self.length_frame.grid(row=1, column=0, columnspan=4, sticky='ew') self.setOptionValues() # width and height are of plot, in pixels def getOutputHandler(self, pixel_width, pixel_height, unit_width=1.0, unit_height=1.0, fonts=None): if not fonts: fonts = [] else: fonts = list(fonts) for n in range(len(fonts)): if fonts[n] == 'Times': fonts[n] = 'Times-Roman' self.setOptionValues() if not self.file_name: showError('No file', 'No file specified', parent=self) return None x_scaling = y_scaling = 1.0 if self.scaling_choice != scaling_choices[0]: try: x_scaling = float(self.x_scaling) except: showError('Bad X Scaling', 'Specified X Scaling must be floating point', parent=self) return None try: y_scaling = float(self.y_scaling) except: showError('Bad Y Scaling', 'Specified Y Scaling must be floating point', parent=self) return None if os.path.exists(self.file_name): if not showYesNo('File exists', 'File "%s" exists, overwrite?' % self.file_name, parent=self): return None if self.paper_type == Output.other_paper_type: paper_size = self.other_size + [ self.other_unit ] else: paper_size = paper_sizes[self.paper_type] output_scaling = self.scaling / 100.0 border_font = self.border_font (font, size) = border_font.split() size = int(size) border_text = {} for decoration in self.border_decoration: if decoration == 'Time and Date': location = 'se' text = time.ctime(time.time()) elif decoration == 'File Name': location = 'sw' text = self.file_name else: continue # should not be here border_text[location] = (text, font, size) if self.title: location = 'n' border_text[location] = (self.title, font, size+6) if font not in fonts: fonts.append(font) if self.haveTicks and self.tick_location == PrintTicks.Outside: axis_label_offset = 2 else: axis_label_offset = 0 outputHandler = PrintHandler.getOutputHandler(self.file_name, pixel_width, pixel_height, unit_width, unit_height, scaling_choice=self.scaling_choice, output_scaling=output_scaling, w_scaling=x_scaling, h_scaling=y_scaling, paper_size=paper_size, paper_orientation=self.paper_orientation, output_style=self.output_style, output_format=self.output_format, border_text=border_text, x_axis_label=self.x_axis_label, y_axis_label=self.y_axis_label, axis_label_font=font, axis_label_size=size, axis_label_offset=axis_label_offset, fonts=fonts, linewidth=self.linewidth, do_outline_box=self.doOutlineBox) return outputHandler def getAspectRatio(self): self.setOptionValues() if self.paper_type == Output.other_paper_type: paper_size = self.other_size else: paper_size = paper_sizes[self.paper_type] r = paper_size[1] / paper_size[0] if self.paper_orientation == 'Landscape': r = 1.0 / r return r
class UpdateAdministratorPopup(BasePopup, UpdateAgent): def __init__(self, parent, serverLocation=UPDATE_SERVER_LOCATION, serverDirectory=UPDATE_DIRECTORY, dataFile=UPDATE_DATABASE_FILE): UpdateAgent.__init__(self, serverLocation, serverDirectory, dataFile, admin=1) self.fileTypes = [ FileType('Python', ['*.py']), FileType('C', ['*.c']), FileType('All', ['*']) ] self.fileUpdate = None BasePopup.__init__(self, parent=parent, title='CcpNmr Update Administrator', quitFunc=self.quit) def body(self, guiParent): guiParent.grid_columnconfigure(3, weight=1) self.commentsEntry = Entry(self) self.priorityEntry = IntEntry(self) row = 0 label = Label(guiParent, text='Server location:') label.grid(row=row, column=0, sticky='w') location = '' uid = '' httpDir = '' subDir = '' version = 'None' if self.server: location, uid, httpDir, subDir = self.server.identity version = self.server.version or 'None' self.serverEntry = Entry(guiParent, text=location) self.serverEntry.grid(row=row, column=1, stick='w') label = Label(guiParent, text='User ID:') label.grid(row=row, column=2, sticky='w') self.uidEntry = Entry(guiParent, text=uid) self.uidEntry.grid(row=row, column=3, stick='w') row += 1 label = Label(guiParent, text='HTTP directory:') label.grid(row=row, column=0, sticky='w') self.httpDirEntry = Entry(guiParent, text=httpDir) self.httpDirEntry.grid(row=row, column=1, stick='w') label = Label(guiParent, text='Sub-directory:') label.grid(row=row, column=2, sticky='w') self.subDirEntry = Entry(guiParent, text=subDir) self.subDirEntry.grid(row=row, column=3, stick='w') row += 1 self.localVerLabel = Label(guiParent, text='Local version: %s' % self.version) self.localVerLabel.grid(row=row, column=0, sticky='w') self.serverLabel = Label(guiParent, text='Server version: %s' % version) self.serverLabel.grid(row=row, column=2, sticky='w') row += 1 guiParent.grid_rowconfigure(row, weight=1) headingList = [ 'File', 'Location', 'Date', 'Priority', 'Comments', 'StoredAs', ] editWidgets = [ None, None, None, self.priorityEntry, self.commentsEntry ] editGetCallbacks = [ None, None, None, self.getPriority, self.getComments ] editSetCallbacks = [ None, None, None, self.setPriority, self.setComments ] self.scrolledMatrix = ScrolledMatrix(guiParent, headingList=headingList, multiSelect=True, editWidgets=editWidgets, callback=self.selectCell, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks) self.scrolledMatrix.grid(row=row, column=0, columnspan=4, sticky='nsew') row += 1 texts = [ 'Add\nFiles', 'Remove\nFiles', 'Remove\nAll', 'Query\nServer', 'Commit\nSelected', 'Synchronise\nAll', 'Commit\nNew', 'Quit' ] commands = [ self.addFile, self.removeFile, self.removeAll, self.queryFiles, self.synchroniseSelected, self.synchroniseServer, self.updateServer, self.quit ] self.buttonList = ButtonList(guiParent, texts=texts, commands=commands, expands=1) self.buttonList.grid(row=row, column=0, columnspan=4, sticky='ew') self.update() def getPriority(self, fileUpdate): if fileUpdate: self.priorityEntry.set(fileUpdate.priority) def setPriority(self, event): i = self.priorityEntry.get() if self.fileUpdate: self.fileUpdate.priority = i self.update() def getComments(self, fileUpdate): if fileUpdate: self.commentsEntry.set(fileUpdate.details) def setComments(self, event): text = self.commentsEntry.get() if self.fileUpdate: self.fileUpdate.details = text self.update() def quit(self): self.close() self.destroy() sys.exit() def updateServer(self): if self.server: # Unly upload new updates success = self.server.setFileUpdates() self.serverLabel.set('Server version: %s' % self.server.version) if success: self.queryFiles() def synchroniseSelected(self): if self.server: selectedUpdates = self.scrolledMatrix.currentObjects if selectedUpdates: # Refresh all selected updates success = self.server.setFileUpdates( fileUpdates=selectedUpdates, refresh=True) self.serverLabel.set('Server version: %s' % self.server.version) if success: self.queryFiles() def synchroniseServer(self): if self.server: # Refresh all avaiable updates success = self.server.setFileUpdates(refresh=True) self.serverLabel.set('Server version: %s' % self.server.version) if success: self.queryFiles() def queryFiles(self): if self.server: self.server.getFileUpdates() self.update() def addFile(self): if self.server: fileSelectPopup = FileSelectPopup(self, title='Select Source File', dismiss_text='Cancel', file_types=self.fileTypes, multiSelect=True, selected_file_must_exist=True) filePaths = fileSelectPopup.file_select.getFiles() n = len(self.installRoot) for filePath in filePaths: if self.installRoot != filePath[:n]: showWarning( 'Warning', 'Install root %s not found in file path %s' % (self.installRoot, filePath)) continue filePath = filePath[n + 1:] if filePath: dirName, fileName = splitPath(filePath) if fileName[-3:] == '.py': language = 'python' else: language = 'None' fileUpdate = FileUpdate(self.server, fileName, dirName, language, isNew=True) self.update() def removeFile(self): if self.fileUpdate: self.fileUpdate.delete() self.update() def removeAll(self): if self.server: for fileUpdate in list(self.server.fileUpdates): fileUpdate.delete() self.update() def selectAll(self): if self.server: for fileUpdate in self.server.fileUpdates: fileUpdate.isSelected = 1 self.update() def selectCell(self, object, row, col): self.fileUpdate = object self.updateButtons() def updateButtons(self): buttons = self.buttonList.buttons if self.server: buttons[0].enable() buttons[3].enable() buttons[4].enable() else: buttons[0].disable() buttons[3].disable() buttons[4].disable() if self.server and self.server.fileUpdates: buttons[2].enable() else: buttons[2].disable() if self.fileUpdate: buttons[1].enable() else: buttons[1].disable() def update(self): location = self.serverEntry.get() uid = self.uidEntry.get() httpDir = self.httpDirEntry.get() subDir = self.subDirEntry.get() if self.server: if (location, uid, httpDir, subDir) != self.server.identity: self.setServer(location) self.updateButtons() self.fileUpdate = None textMatrix = [] objectList = [] colorMatrix = [] if self.server: for fileUpdate in self.server.fileUpdates: datum = [] datum.append(fileUpdate.fileName) datum.append(fileUpdate.filePath) datum.append(fileUpdate.date) datum.append(fileUpdate.priority) datum.append(fileUpdate.details) datum.append(fileUpdate.storedAs) textMatrix.append(datum) objectList.append(fileUpdate) if fileUpdate.isNew: colorMatrix.append(5 * ['#B0FFB0']) elif not fileUpdate.getIsUpToDate(): colorMatrix.append(5 * ['#FFB0B0']) else: colorMatrix.append(5 * [None]) self.scrolledMatrix.update(textMatrix=textMatrix, objectList=objectList, colorMatrix=colorMatrix)
class PalesFrame(Frame): """ Frame for handling PALES calculation. Note that the frame uses (or creates) an NmrCalcStore named 'PALES' and linked to the current NmrProject. """ def __init__(self, parent, project, closeButton=False, *args, **kw): self.parent = parent self.project = project self.nmrProject = (project.currentNmrProject or project.newNmrProject(name='PALES')) self.calcStore = None self.run = None self.inpStructure = None self.inpConstraintList = None self.workingDir = None self.waiting = False self.palesMode = None self.resetCalcStore() Frame.__init__(self, parent, *args, **kw) self.expandGrid(0, 0) options = ['Input Data', 'Extra Input', 'View Results'] tabbedFrame = TabbedFrame(self, options=options, grid=(0, 0)) frameA, frameX, frameB = tabbedFrame.frames self.tabbedFrame = tabbedFrame label = Label(tabbedFrame.sideFrame, text='Run Number:', grid=(0, 0), sticky='e') tipText = 'Selects which calculation job or "run" is currently being viewed or edited' self.runPulldown = PulldownList(tabbedFrame.sideFrame, callback=self.changeRun, grid=(0, 1), sticky='e', tipText=tipText) tipTexts = ['Delete the current calculation run settings'] texts = ['Delete Run'] commands = [self.deleteRun] if closeButton: ButtonListClass = UtilityButtonList else: ButtonListClass = ButtonList runButtons = ButtonListClass(tabbedFrame.sideFrame, texts=texts, tipTexts=tipTexts, commands=commands, sticky='e', grid=(0, 2)) # Input data frameA.expandGrid(2, 1) row = 0 label = Label(frameA, text='Pales mode:', grid=(row, 0), sticky='w') self.palesModePulldown = PulldownList(frameA, callback=self.changePalesMode, grid=(row, 1)) tipTexts = [ 'Make a setup for a new calculation run', 'Make a new calculation run by copying the current one', ] texts = ['New Run', 'Copy Run'] commands = [self.newRun, self.copyRun] if closeButton: ButtonListClass = UtilityButtonList else: ButtonListClass = ButtonList runButtons = ButtonList(frameA, texts=texts, tipTexts=tipTexts, commands=commands, sticky='e', grid=(row, 2)) runButtons.buttons[0].config(bg='#B0FFB0') row += 1 subframe1 = LabelFrame(frameA, text='Description', grid=(row, 0), gridSpan=(1, 3)) subframe1.expandGrid(0, 1) self.modeDescription = Label(subframe1, grid=(row, 0), sticky='w') row += 1 # setup generic table headings, justification and widget getters/setters self.inputMatrix = GenericDataMatrix(frameA, progParameters) self.inputMatrix.grid(row=row, column=0, columnspan=3, sticky='nsew') row += 1 label = Label(frameA, text='Comments:', grid=(row, 0)) self.detailsEntryIn = Entry(frameA, grid=(row, 1), gridSpan=(1, 2), sticky="ew") self.detailsEntryIn.bind('<Leave>', self.changeDetailsIn) row += 1 button = Button(frameA, text='Select working dir:', bd=1, command=self.selectWorkingDir, grid=(row, 0), sticky="ew") self.workingDirEntry = Entry(frameA, text='.', grid=(row, 1), gridSpan=(1, 2), width=48, sticky="ew", bd=1) row += 1 button = Button(frameA, text='Execute Pales:', bd=1, command=self.executePales, grid=(row, 0), gridSpan=(1, 3), sticky="new") # Extra input # setup generic table headings, justification and widget getters/setters frameX.expandGrid(0, 0) self.extraInputMatrix = GenericDataMatrix(frameX, progParameters) self.extraInputMatrix.grid(row=0, column=0, sticky='nsew') # View Results frameB.expandGrid(7, 1) row = 0 subframe1 = LabelFrame(frameB, text='Command Options:', grid=(row, 0), gridSpan=(1, 4)) # gridSpan=(1,2)) subframe1.expandGrid(0, 1) self.palesOptionsLabel = Label(subframe1, grid=(row, 0), sticky='w') row += 1 div = LabelDivider(frameB, text='Data', grid=(row, 0), gridSpan=(1, 4)) row += 1 self.outputMatrix = GenericDataMatrix(frameB, progParameters, initialRows=4) self.outputMatrix.grid(row=0, column=0, sticky='nsew') self.outputMatrix.grid(row=row, column=0, columnspan=(4), sticky='nsew') row += 1 button = Button(frameB, text='View Selected', bd=1, command=self.viewPalesData, grid=(row, 0), gridSpan=(1, 4), sticky="ew") row += 1 label = Label(frameB, text='Comments:', grid=(row, 0), sticky="w") self.detailsEntry = Entry(frameB, grid=(row, 1), gridSpan=(1, 3), sticky="ew") self.detailsEntry.bind('<Leave>', self.changeDetails) row += 1 subframe2 = LabelFrame(frameB, text='Calculated Order Matrix:', grid=(row, 0), gridSpan=(1, 4)) #subframe2.grid_columnconfigure(5, weight=1) subframe2.expandGrid(1, 5) label = Label(subframe2, text='Daxial', grid=(0, 0), sticky='ew') label = Label(subframe2, text='Drhombic', grid=(0, 1), sticky='ew') label = Label(subframe2, text='Psi', grid=(0, 2), sticky='ew') label = Label(subframe2, text='Phi', grid=(0, 3), sticky='ew') label = Label(subframe2, text='Theta', grid=(0, 4), sticky='ew') self.outputTensorLabels = ll = [] for ii in range(5): label = Label(subframe2, text='<None>', grid=(1, ii), sticky='ew') ll.append(label) row += 1 div = LabelDivider(frameB, text='Program Output', grid=(row, 0), gridSpan=(1, 4), sticky='sew') #textFrame1 = LabelFrame(frameB, text='Pales Output File', grid=(5,0), # gridSpan=(1,6), sticky='nsew') #textFrame1.expandGrid(0,0) row += 1 self.palesOutputText = ScrolledText(frameB, xscroll=False) self.palesOutputText.grid(row=row, column=0, columnspan=4, sticky='nsew') self.updateAfter() self.administerNotifiers(self.parent.registerNotify) def updatePalesOptions(self): palesMode = self.palesMode dd = palesModes.get(palesMode) # NBNB TBD def resetCalcStore(self, calcStore=None): """ Reset self.calcStore if missing or deleted """ if calcStore is None or calcStore is self.calcStore: nmrProject = self.nmrProject calcStore = self.project.findFirstNmrCalcStore( nmrProject=nmrProject, name='PALES') if calcStore is None: calcStore = self.project.newNmrCalcStore( nmrProject=self.nmrProject, name='PALES') if self.calcStore is not calcStore: self.calcStore = calcStore if self.run is not None: self.run = None self.updateAfter() def selectWorkingDir(self): popup = FileSelectPopup(self, show_file=False) directory = popup.getDirectory() if directory: self.workingDirEntry.set(directory) popup.destroy() self.updateAfter() def changeDetails(self, event): if self.run: value = self.detailsEntry.get().strip() or None if value != self.run.details: self.run.details = value def changeDetailsIn(self, event): if self.run: value = self.detailsEntryIn.get().strip() or None if value != self.run.details: self.run.details = value def updateDetails(self): if self.run: text = self.run.details else: text = '' self.detailsEntry.set(text) self.detailsEntryIn.set(text) def changePalesMode(self, mode): if mode and (mode is not self.palesMode): run = self.run if runIsEditable(run): palesModeObj = run.findFirstRunParameter(name='Pales Mode') if palesModeObj is None: palesModeObj = run.newRunParameter(name='Pales Mode', ioRole='input') palesModeObj.textValue = mode self.palesMode = mode self.initialiseRun() self.updateAfter() def updatePalesModes(self, obj=None): names = [] index = 0 modes = [] for tag in palesModeList: modeInfo = palesModes[tag] names.append(modeInfo['text']) modes.append(tag) if obj is not None: mode = obj else: mode = self.palesMode try: index = modes.index(mode) except ValueError: index = 0 self.palesMode = modes[index] self.palesModePulldown.setup(names, modes, index) tag = modes[index] self.modeDescription.set(palesModes[tag].get('info')) def copyRun(self): # TBD: Inputs only? if self.run: self.configure(cursor="watch") run = copySubTree(self.run, self.calcStore) run.status = 'provisional' # remove output for data in run.findAllData(ioRole='output'): data.delete() for parObj in run.findAllRunParameters(ioRole='output'): parObj.delete() self.run = run self.updateAfter() self.after_idle(lambda: self.configure(cursor="")) def newRun(self): if self.calcStore: self.run = self.calcStore.newRun(status='provisional') self.initialiseRun() self.updateAfter() def initialiseRun(self): """ Create objects for known parameters """ print '### initialiseRun', self.run and runIsEditable(self.run) run = self.run if run is None or not runIsEditable(run): return # set up tag = self.palesMode modeData = palesModes[tag] currentTags = set(modeData['fixedpar'] + modeData['mandatories'] + modeData['optionals']) # set palesMode if missing self.updatePalesModes() # remove parameters no longer needed for obj in run.runParameters: if obj.name not in currentTags: obj.delete() for obj in run.data: if obj.name not in currentTags: obj.delete() self.inputMatrix.updateGeneric(run=run, paramList=(modeData['fixedpar'] + modeData['mandatories'])) self.extraInputMatrix.updateGeneric(run=run, paramList=modeData['optionals']) for ii, name in enumerate(modeData['fixedpar']): value = modeData['fixedvalues'][ii] code, parType, default, info = progParameters[name] self.inputMatrix.setupRunObject(run, name, code, parType, default, info, value=value) def getPalesOptions(self): run = self.run if run is None: return palesMode = self.palesMode return 'NBNB TBD' def changeRun(self, run): print '### changeRun', self.run, run if run and (run is not self.run): self.run = run self.updateAfter() def deleteRun(self): if self.run: msg = 'Really delete calculation run %d?' % self.run.serial if showOkCancel('Query', msg, parent=self): self.run.delete() self.run = None self.updateAfter() def updateRunsAfter(self, obj=None): print '### updateRunsAfter', obj, self.waiting if self.waiting: return else: self.waiting = True self.after_idle(self.updateRuns) def updateRunDataAfter(self, obj=None): print '### updateRunDataAfter', obj if obj is None: run = None else: run = obj.run self.updateRunAfter(run=run) def updateRunAfter(self, run=None): """ update all if run is curent run, otherwise update run pulldown only """ print '### updateRunAfter', run, self.waiting if self.waiting: return elif run is None: return elif run is self.run: self.waiting = True self.after_idle(self.update) else: self.waiting = True self.after_idle(self.updateRuns) def updateAfter(self, obj=None): if self.waiting: return self.waiting = True self.configure(cursor="watch") self.after_idle(self.update) def updateRuns(self, run=None): """ Update run pulldown only """ print '### updateRuns', run names = [] index = 0 runs = [] run = self.run if self.calcStore is None or self.calcStore.isDeleted: self.resetCalcStore() runs = self.calcStore.sortedRuns() if runs: if run not in runs: run = runs[-1] index = runs.index(run) names = [] for r in runs: if runIsEditable(r): names.append('%d' % r.serial) else: names.append('%d (uneditable)' % r.serial) else: run = None if run is not self.run: self.changeRun(run) print '###', names, runs, index self.runPulldown.setup(names, runs, index) self.waiting = False def update(self, obj=None): print '### update', self.run self.updateRuns(obj) run = self.run if run is None: palesMode = self.palesMode = None self.palesOptionsLabel.set('') for label in self.outputTensorLabels: label.set('<None>') self.palesOutputText.setText() else: # Pales command NBNB TBD #valueObj = run.findFirstRunParameter(code='command') #if valueObj is None: # self.palesCommandLabel.set('') #else: # self.palesCommandLabel.set(valueObj.textValue) palesModeObj = run.findFirstRunParameter(ioRole='input', name='Pales Mode') if palesModeObj is None: palesMode = None else: palesMode = palesModeObj.textValue # Output orientation matrix keywords = ('dAxialOut', 'dRhombicOut', 'psiOut', 'phiOut', 'thetaOut') for ii, label in enumerate(self.outputTensorLabels): keyword = keywords[ii] valueObj = run.findFirstRunParameter(code=keyword, ioRole='output') if valueObj is None: label.set('<None>') else: label.set(valueObj.floatValue) # Pales output text valueObj = run.findFirstRunParameter(ioRole='output', name='Output Text') if valueObj: self.palesOutputText.setText(valueObj.textValue) else: self.palesOutputText.setText() self.updatePalesModes(palesMode) self.palesOptionsLabel.set(self.getPalesOptions()) #self.updateInputMatrix() #self.updateExtraInputMatrix() #self.updateOutputMatrix() self.inputMatrix.updateGeneric() self.extraInputMatrix.updateGeneric() self.outputMatrix.updateGeneric() self.updateDetails() self.after_idle(lambda: self.configure(cursor="")) self.waiting = False def administerNotifiers(self, notifyFunc): notifyFunc(self.updateRunsAfter, 'ccp.nmr.NmrCalc.NmrCalcStore', 'delete') notifyFunc(self.updateRunsAfter, 'ccp.nmr.NmrCalc.Run', '__init__') notifyFunc(self.updateRunAfter, 'ccp.nmr.NmrCalc.Run', 'delete') notifyFunc(self.updateRunAfter, 'ccp.nmr.NmrCalc.Run', 'setDetails') NC = 'ccp.nmr.NmrCalc.' nmrCalcClasses = [ 'RunParameter', 'EnergyTerm', 'ConstraintStoreData', 'ViolationListData', 'MolSystemData', 'MolResidueData', 'SpectrumData', 'PeakListData', 'SpinSystemData', 'StructureEnsembleData', 'ExternalData', 'FloatMatrixData', 'TensorData', 'MeasurementListData', 'DerivedListData', ] for clazz in nmrCalcClasses: notifyFunc(self.updateRunDataAfter, NC + clazz, '') def destroy(self): self.administerNotifiers(self.parent.unregisterNotify) Frame.destroy(self) def executePales(self): # NBNB TBD pass def viewPalesData(self): # NBNB TBD valueObj = self.outputMatrix if isinstance(valueObj, NmrCalc.ExternalData): dataStore = valueObj.dataStore if dataStore is not None: wb = WebBrowser(self, name='PalesData') wb.open(dataStore.fullPath) elif isinstance(valueObj, NmrCalc.MeasurementListData): mm = valueObj.measurementList if mm is not None: popup = self.parent.editMeasurementLists() popup.tabbedFrame.select(1) popup.setMeasurementList(mm) elif isinstance(valueObj, NmrCalc.StructureEnsembleData): models = valueObj.models if len(models) == 1: popup = self.parent.editStructures() popup.tabbedFrame.select(3) popup.changeModel(models[0]) popup.changeTab(3) elif isinstance(valueObj, NmrCalc.ConstraintStoreData): constraintLists = valueObj.constraintLists if len(constraintLists) == 1: popup = self.parent.browseConstraints() popup.tabbedFrame.select(2) popup.changeRestraintList(constraintLists[0])
class CingGui(BasePopup): def __init__(self, parent, options, *args, **kw): # Fill in below variable once run generates some results self.haveResults = None # store the options self.options = options BasePopup.__init__(self, parent=parent, title='CING Setup', **kw) # self.setGeometry(850, 750, 50, 50) self.project = None # self.tk_strictMotif( True) self.updateGui() # end def __init__ def body(self, guiFrame): row = 0 col = 0 # frame = Frame( guiFrame ) # frame.grid(row=row, column=col, sticky='news') self.menuBar = Menu(guiFrame) self.menuBar.grid(row=row, column=col, sticky='ew') #---------------------------------------------------------------------------------- # Project frame #---------------------------------------------------------------------------------- # guiFrame.grid_columnconfigure(row, weight=1) # frame = LabelFrame(guiFrame, text='Project', font=medFont) row = +1 col = 0 frame = LabelFrame(guiFrame, text='Project', **labelFrameAttributes) print '>', frame.keys() frame.grid(row=row, column=col, sticky='nsew') frame.grid_columnconfigure(2, weight=1) # frame.grid_rowconfigure(0, weight=1) srow = 0 self.projectOptions = [ 'old', 'new from PDB', 'new from CCPN', 'new from CYANA' ] self.projOptionsSelect = RadioButtons(frame, selected_index=0, entries=self.projectOptions, direction='vertical', select_callback=self.updateGui) self.projOptionsSelect.grid(row=srow, column=0, rowspan=len(self.projectOptions), columnspan=2, sticky='w') if self.options.name: text = self.options.name else: text = '' # end if self.projEntry = Entry(frame, bd=1, text=text, returnCallback=self.updateGui) self.projEntry.grid(row=srow, column=2, columnspan=2, sticky='ew') # self.projEntry.bind('<Key>', self.updateGui) self.projEntry.bind('<Leave>', self.updateGui) projButton = Button(frame, bd=1, command=self.chooseOldProjectFile, text='browse') projButton.grid(row=srow, column=3, sticky='ew') srow += 1 self.pdbEntry = Entry(frame, bd=1, text='') self.pdbEntry.grid(row=srow, column=2, sticky='ew') self.pdbEntry.bind('<Leave>', self.updateGui) pdbButton = Button(frame, bd=1, command=self.choosePdbFile, text='browse') pdbButton.grid(row=srow, column=3, sticky='ew') srow += 1 self.ccpnEntry = Entry(frame, bd=1, text='') self.ccpnEntry.grid(row=srow, column=2, sticky='ew') self.ccpnEntry.bind('<Leave>', self.updateGui) ccpnButton = Button(frame, bd=1, command=self.chooseCcpnFile, text='browse') ccpnButton.grid(row=srow, column=3, sticky='ew') srow += 1 self.cyanaEntry = Entry(frame, bd=1, text='') self.cyanaEntry.grid(row=srow, column=2, sticky='ew') self.cyanaEntry.bind('<Leave>', self.updateGui) cyanaButton = Button(frame, bd=1, command=self.chooseCyanaFile, text='browse') cyanaButton.grid(row=srow, column=3, sticky='ew') #Empty row srow += 1 label = Label(frame, text='') label.grid(row=srow, column=0, sticky='nw') srow += 1 label = Label(frame, text='Project name:') label.grid(row=srow, column=0, sticky='nw') self.nameEntry = Entry(frame, bd=1, text='') self.nameEntry.grid(row=srow, column=2, sticky='w') #Empty row srow += 1 label = Label(frame, text='') label.grid(row=srow, column=0, sticky='nw') srow += 1 self.openProjectButton = Button(frame, command=self.openProject, text='Open Project', **actionButtonAttributes) self.openProjectButton.grid(row=srow, column=0, columnspan=4, sticky='ew') #---------------------------------------------------------------------------------- # status #---------------------------------------------------------------------------------- # guiFrame.grid_columnconfigure(1, weight=0) srow = 0 frame = LabelFrame(guiFrame, text='Status', **labelFrameAttributes) frame.grid(row=srow, column=1, sticky='wnes') self.projectStatus = Text(frame, height=11, width=70, borderwidth=0, relief='flat') self.projectStatus.grid(row=0, column=0, sticky='wen') #Empty row srow += 1 label = Label(frame, text='') label.grid(row=srow, column=0, sticky='nw') srow += 1 self.closeProjectButton = Button(frame, command=self.closeProject, text='Close Project', **actionButtonAttributes) self.closeProjectButton.grid(row=srow, column=0, columnspan=4, sticky='ew') #---------------------------------------------------------------------------------- # Validate frame #---------------------------------------------------------------------------------- row += 1 col = 0 frame = LabelFrame(guiFrame, text='Validate', **labelFrameAttributes) # frame = LabelFrame(guiFrame, text='Validate', font=medFont) frame.grid(row=row, column=col, sticky='nsew') # frame.grid_columnconfigure(2, weight=1) frame.grid_rowconfigure(0, weight=1) srow = 0 # label = Label(frame, text='validation') # label.grid(row=srow,column=0,sticky='nw') # # self.selectDoValidation = CheckButton(frame) # self.selectDoValidation.grid(row=srow, column=1,sticky='nw' ) # self.selectDoValidation.set(True) # # srow += 1 # label = Label(frame, text='') # label.grid(row=srow,column=0,sticky='nw') # # srow += 1 label = Label(frame, text='checks') label.grid(row=srow, column=0, sticky='nw') self.selectCheckAssign = CheckButton(frame) self.selectCheckAssign.grid(row=srow, column=1, sticky='nw') self.selectCheckAssign.set(True) label = Label(frame, text='assignments and shifts') label.grid(row=srow, column=2, sticky='nw') # srow += 1 # self.selectCheckQueen = CheckButton(frame) # self.selectCheckQueen.grid(row=srow, column=4,sticky='nw' ) # self.selectCheckQueen.set(False) # label = Label(frame, text='QUEEN') # label.grid(row=srow,column=5,sticky='nw') # # queenButton = Button(frame, bd=1,command=None, text='setup') # queenButton.grid(row=srow,column=6,sticky='ew') srow += 1 self.selectCheckResraint = CheckButton(frame) self.selectCheckResraint.grid(row=srow, column=1, sticky='nw') self.selectCheckResraint.set(True) label = Label(frame, text='restraints') label.grid(row=srow, column=2, sticky='nw') srow += 1 self.selectCheckStructure = CheckButton(frame) self.selectCheckStructure.grid(row=srow, column=1, sticky='nw') self.selectCheckStructure.set(True) label = Label(frame, text='structural') label.grid(row=srow, column=2, sticky='nw') srow += 1 self.selectMakeHtml = CheckButton(frame) self.selectMakeHtml.grid(row=srow, column=1, sticky='nw') self.selectMakeHtml.set(True) label = Label(frame, text='generate HTML') label.grid(row=srow, column=2, sticky='nw') srow += 1 self.selectCheckScript = CheckButton(frame) self.selectCheckScript.grid(row=srow, column=1, sticky='nw') self.selectCheckScript.set(False) label = Label(frame, text='user script') label.grid(row=srow, column=0, sticky='nw') self.validScriptEntry = Entry(frame, bd=1, text='') self.validScriptEntry.grid(row=srow, column=2, columnspan=3, sticky='ew') scriptButton = Button(frame, bd=1, command=self.chooseValidScript, text='browse') scriptButton.grid(row=srow, column=5, sticky='ew') srow += 1 label = Label(frame, text='ranges') label.grid(row=srow, column=0, sticky='nw') self.rangesEntry = Entry(frame, text='') self.rangesEntry.grid(row=srow, column=2, columnspan=3, sticky='ew') # self.validScriptEntry = Entry(frame, bd=1, text='') # self.validScriptEntry.grid(row=srow,column=3,sticky='ew') # # scriptButton = Button(frame, bd=1,command=self.chooseValidScript, text='browse') # scriptButton.grid(row=srow,column=4,sticky='ew') srow += 1 texts = ['Run Validation', 'View Results', 'Setup QUEEN'] commands = [self.runCing, None, None] buttonBar = ButtonList(frame, texts=texts, commands=commands, expands=True) buttonBar.grid(row=srow, column=0, columnspan=6, sticky='ew') for button in buttonBar.buttons: button.config(**actionButtonAttributes) # end for self.runButton = buttonBar.buttons[0] self.viewResultButton = buttonBar.buttons[1] self.queenButton = buttonBar.buttons[2] #---------------------------------------------------------------------------------- # Miscellaneous frame #---------------------------------------------------------------------------------- row += 0 col = 1 # frame = LabelFrame(guiFrame, text='Miscellaneous', font=medFont) frame = LabelFrame(guiFrame, text='Miscellaneous', **labelFrameAttributes) frame.grid(row=row, column=col, sticky='news') frame.grid_columnconfigure(2, weight=1) frame.grid_columnconfigure(4, weight=1, minsize=30) frame.grid_rowconfigure(0, weight=1) # Exports srow = 0 label = Label(frame, text='export to') label.grid(row=srow, column=0, sticky='nw') self.selectExportXeasy = CheckButton(frame) self.selectExportXeasy.grid(row=srow, column=1, sticky='nw') self.selectExportXeasy.set(True) label = Label(frame, text='Xeasy, Sparky, TALOS, ...') label.grid(row=srow, column=2, sticky='nw') srow += 1 self.selectExportCcpn = CheckButton(frame) self.selectExportCcpn.grid(row=srow, column=1, sticky='nw') self.selectExportCcpn.set(True) label = Label(frame, text='CCPN') label.grid(row=srow, column=2, sticky='nw') srow += 1 self.selectExportQueen = CheckButton(frame) self.selectExportQueen.grid(row=srow, column=1, sticky='nw') self.selectExportQueen.set(True) label = Label(frame, text='QUEEN') label.grid(row=srow, column=2, sticky='nw') srow += 1 self.selectExportRefine = CheckButton(frame) self.selectExportRefine.grid(row=srow, column=1, sticky='nw') self.selectExportRefine.set(True) label = Label(frame, text='refine') label.grid(row=srow, column=2, sticky='nw') srow += 1 label = Label(frame, text='') label.grid(row=srow, column=0, sticky='nw') # User script srow += 1 label = Label(frame, text='user script') label.grid(row=srow, column=0, sticky='nw') self.selectMiscScript = CheckButton(frame) self.selectMiscScript.grid(row=srow, column=1, sticky='nw') self.selectMiscScript.set(False) self.miscScriptEntry = Entry(frame, bd=1, text='') self.miscScriptEntry.grid(row=srow, column=3, sticky='ew') script2Button = Button(frame, bd=1, command=self.chooseMiscScript, text='browse') script2Button.grid(row=srow, column=4, sticky='ew') srow += 1 texts = ['Export', 'Run Script'] commands = [None, None] buttonBar = ButtonList(frame, texts=texts, commands=commands, expands=True) buttonBar.grid(row=srow, column=0, columnspan=5, sticky='ew') for button in buttonBar.buttons: button.config(**actionButtonAttributes) # end for self.exportButton = buttonBar.buttons[0] self.scriptButton = buttonBar.buttons[1] #---------------------------------------------------------------------------------- # Textarea #---------------------------------------------------------------------------------- row += 1 guiFrame.grid_rowconfigure(row, weight=1) self.outputTextBox = ScrolledText(guiFrame) self.outputTextBox.grid(row=row, column=0, columnspan=2, sticky='nsew') self.redirectConsole() #---------------------------------------------------------------------------------- # Buttons #---------------------------------------------------------------------------------- row += 1 col = 0 texts = ['Quit', 'Help'] commands = [self.close, None] self.buttonBar = ButtonList(guiFrame, texts=texts, commands=commands, expands=True) self.buttonBar.grid(row=row, column=col, columnspan=2, sticky='ew') # self.openProjectButton = self.buttonBar.buttons[0] # self.closeProjectButton = self.buttonBar.buttons[1] # self.runButton = self.buttonBar.buttons[0] # self.viewResultButton = self.buttonBar.buttons[1] for button in self.buttonBar.buttons: button.config(**actionButtonAttributes) # end for # end def body def getGuiOptions(self): projectName = self.projEntry.get() index = self.projOptionsSelect.getIndex() if index > 0: makeNewProject = True projectImport = None if index > 1: i = index - 2 format = ['PDB', 'CCPN', 'CYANA'][i] file = [self.pdbEntry, self.ccpnEntry, self.cyanaEntry][i].get() if not file: showWarning('Failure', 'No %s file selected' % format) return # end if projectImport = (format, file) # end if else: # Chould also check that any old project file exists makeNewProject = False projectImport = None # end if doValidation = self.selectDoValidation.get() checks = [] if doValidation: if self.selectCheckAssign.get(): checks.append('assignments') # end if if self.selectCheckResraint.get(): checks.append('restraints') # end if if self.selectCheckStructure.get(): checks.append('structural') # end if if self.selectMakeHtml.get(): checks.append('HTML') # end if if self.selectCheckScript.get(): script = self.validScriptEntry.get() if script: checks.append(('script', script)) # end if # end if if self.selectCheckQueen.get(): checks.append('queen') # end if # end if exports = [] if self.selectExportXeasy.get(): exports.append('Xeasy') # end if if self.selectExportCcpn.get(): exports.append('CCPN') # end if if self.selectExportQueen.get(): exports.append('QUEEN') # end if if self.selectExportRefine.get(): exports.append('refine') # end if miscScript = None if self.selectMiscScript.get(): script = self.miscScriptEntry.get() if script: miscScript = script # end if # end if return projectName, makeNewProject, projectImport, doValidation, checks, exports, miscScript # end def getGuiOptions def runCing(self): options = self.getGuiOptions() if options: projectName, makeNewProject, projectImport, doValidation, checks, exports, miscScript = options print 'Project name:', projectName print 'Make new project?', makeNewProject print 'Import source:', projectImport print 'Do vailidation?', doValidation print 'Validation checks:', ','.join(checks) print 'Export to:', ','.join(exports) print 'User script:', miscScript # end if # end def runCing # else there was already an error message def chooseOldProjectFile(self): fileTypes = [FileType('CING', ['project.xml']), FileType('All', ['*'])] popup = FileSelectPopup(self, file_types=fileTypes, title='Select CING project file', dismiss_text='Cancel', selected_file_must_exist=True) fileName = popup.getFile() # dirName = popup.getDirectory() if len(fileName) > 0: # Put text into entry,name widgets dummy, name = cing.Project.rootPath(fileName) self.projEntry.configure(state='normal') self.projEntry.set(fileName) self.nameEntry.configure(state='normal') self.nameEntry.set(name) self.nameEntry.configure(state='disabled') # choose the correct radiobutton self.projOptionsSelect.setIndex(0) self.updateGui() # end if #nd if popup.destroy() # end def chooseOldProjectFile def choosePdbFile(self): fileTypes = [FileType('PDB', ['*.pdb']), FileType('All', ['*'])] popup = FileSelectPopup(self, file_types=fileTypes, title='PDB file', dismiss_text='Cancel', selected_file_must_exist=True) fileName = popup.getFile() if len(fileName) > 0: # Put text into entry widget self.pdbEntry.configure(state='normal') self.pdbEntry.set(fileName) # Put text into name widget _dir, name, dummy = nTpath(fileName) self.nameEntry.configure(state='normal') self.nameEntry.set(name) # choose the correct radiobutton self.projOptionsSelect.setIndex(1) self.updateGui() #end if popup.destroy() # end def choosePdbFile def chooseCcpnFile(self): fileTypes = [FileType('XML', ['*.xml']), FileType('All', ['*'])] popup = FileSelectPopup(self, file_types=fileTypes, title='CCPN project XML file', dismiss_text='Cancel', selected_file_must_exist=True) fileName = popup.getFile() if len(fileName) > 0: self.pdbEntry.configure(state='normal') self.pdbEntry.set(fileName) self.projOptionsSelect.setIndex(1) _dir, name, dummy = nTpath(fileName) self.nameEntry.set(name) #end if self.ccpnEntry.set(fileName) self.projOptionsSelect.setIndex(2) popup.destroy() # end def chooseCcpnFile def chooseCyanaFile(self): # Prepend default Cyana file extension below fileTypes = [ FileType('All', ['*']), ] popup = FileSelectPopup(self, file_types=fileTypes, title='CYANA fproject file', dismiss_text='Cancel', selected_file_must_exist=True) fileName = popup.getFile() self.cyanaEntry.set(fileName) self.projOptionsSelect.setIndex(3) popup.destroy() # end def chooseCyanaFile def chooseValidScript(self): # Prepend default Cyana file extension below fileTypes = [ FileType('All', ['*']), ] popup = FileSelectPopup(self, file_types=fileTypes, title='Script file', dismiss_text='Cancel', selected_file_must_exist=True) fileName = popup.getFile() self.validScriptEntry.set(fileName) popup.destroy() # end def chooseValidScript def chooseMiscScript(self): # Prepend default Cyana file extension below fileTypes = [ FileType('All', ['*']), ] popup = FileSelectPopup(self, file_types=fileTypes, title='Script file', dismiss_text='Cancel', selected_file_must_exist=True) fileName = popup.getFile() self.miscScriptEntry.set(fileName) popup.destroy() # end def chooseMiscScript def openProject(self): projOption = self.projOptionsSelect.get() if projOption == self.projectOptions[0]: self.openOldProject() elif projOption == self.projectOptions[1]: self.initPdb() # end if if self.project: self.project.gui = self # end if self.updateGui() #end def def openOldProject(self): fName = self.projEntry.get() if not os.path.exists(fName): nTerror('Error: file "%s" does not exist\n', fName) #end if if self.project: self.closeProject() # end if self.project = cing.Project.open(name=fName, status='old', verbose=False) #end def def initPdb(self): fName = self.pdbEntry.get() if not os.path.exists(fName): nTerror('Error: file "%s" does not exist\n', fName) #end if self.project = cing.Project.open(self.nameEntry.get(), status='new') self.project.initPDB(pdbFile=fName, convention='PDB') #end def def closeProject(self): if self.project: self.project.close() # end if self.project = None self.updateGui() #end def def updateGui(self, event=None): projOption = self.projOptionsSelect.get() buttons = self.buttonBar.buttons # Disable entries for e in [ self.projEntry, self.pdbEntry, self.ccpnEntry, self.cyanaEntry, self.nameEntry ]: e.configure(state='disabled') #end for if projOption == self.projectOptions[0]: # Enable entries self.projEntry.configure(state='normal') if (len(self.projEntry.get()) > 0): self.openProjectButton.enable() self.runButton.enable() else: self.openProjectButton.disable() self.runButton.disable() #end if elif projOption == self.projectOptions[1]: # Enable entries self.pdbEntry.configure(state='normal') self.nameEntry.configure(state='normal') if (len(self.pdbEntry.get()) > 0 and len(self.nameEntry.get()) > 0): buttons[0].enable() buttons[1].enable() else: buttons[0].disable() buttons[1].disable() #end if #end if elif projOption == self.projectOptions[2]: # Enable entries self.ccpnEntry.configure(state='normal') self.nameEntry.configure(state='normal') if (len(self.ccpnEntry.get()) > 0 and len(self.nameEntry.get()) > 0): buttons[0].enable() buttons[1].enable() else: buttons[0].disable() buttons[1].disable() #end if #end if elif projOption == self.projectOptions[3]: # Enable entries self.cyanaEntry.configure(state='normal') self.nameEntry.configure(state='normal') if (len(self.cyanaEntry.get()) > 0 and len(self.nameEntry.get()) > 0): buttons[0].enable() buttons[1].enable() else: buttons[0].disable() buttons[1].disable() #end if #end if self.projectStatus.clear() if not self.project: self.projectStatus.setText('No open project') self.closeProjectButton.setText('Close Project') self.closeProjectButton.disable() else: self.projectStatus.setText(self.project.format()) self.closeProjectButton.enable() self.closeProjectButton.setText( sprintf('Close Project "%s"', self.project.name)) #end if #end def def redirectConsole(self): #pipe = TextPipe(self.inputTextBox.text_area) #sys.stdin = pipe pipe = TextPipe(self.outputTextBox.text_area) sys.stdout = pipe nTmessage.stream = pipe # end def redirectConsole # sys.stderr = pipe def resetConsole(self): #sys.stdin = stdin nTmessage.stream = stdout sys.stdout = stdout sys.stderr = stderr # end def resetConsole def open(self): self.redirectConsole() BasePopup.open(self) # end def open def close(self): geometry = self.getGeometry() self.resetConsole() BasePopup.close(self) print 'close:', geometry sys.exit(0) # remove later # end def close def destroy(self): geometry = self.getGeometry() self.resetConsole() BasePopup.destroy(self) print 'destroy:', geometry sys.exit(0) # remove later
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
class CingFrame(NmrSimRunFrame): def __init__(self, parent, application, *args, **kw): project = application.project simStore = project.findFirstNmrSimStore(application=APP_NAME) or \ project.newNmrSimStore(application=APP_NAME, name=APP_NAME) self.application = application self.residue = None self.structure = None self.serverCredentials = None self.iCingBaseUrl = DEFAULT_URL self.resultsUrl = None self.chain = None self.nmrProject = application.nmrProject self.serverDone = False NmrSimRunFrame.__init__(self, parent, project, simStore, *args, **kw) # # # # # # New Structure Frame # # # # # self.structureFrame.grid_forget() tab = self.tabbedFrame.frames[0] frame = Frame(tab, grid=(1,0)) frame.expandGrid(2,1) div = LabelDivider(frame, text='Structures', grid=(0,0), gridSpan=(1,2)) label = Label(frame, text='Ensemble: ', grid=(1,0)) self.structurePulldown = PulldownList(frame, callback=self.changeStructure, grid=(1,1)) headingList = ['Model','Use'] editWidgets = [None,None] editGetCallbacks = [None,self.toggleModel] editSetCallbacks = [None,None,] self.modelTable = ScrolledMatrix(frame, grid=(2,0), gridSpan=(1,2), callback=self.selectStructModel, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, headingList=headingList) texts = ['Activate Selected','Inactivate Selected'] commands = [self.activateModels, self.disableModels] buttons = ButtonList(frame, texts=texts, commands=commands, grid=(3,0), gridSpan=(1,2)) # # # # # # Submission frame # # # # # # tab = self.tabbedFrame.frames[1] tab.expandGrid(1,0) frame = LabelFrame(tab, text='Server Job Submission', grid=(0,0)) frame.expandGrid(None,2) srow = 0 label = Label(frame, text='iCing URL:', grid=(srow, 0)) urls = [DEFAULT_URL,] self.iCingBaseUrlPulldown = PulldownList(frame, texts=urls, objects=urls, index=0, grid=(srow,1)) srow +=1 label = Label(frame, text='Results File:', grid=(srow, 0)) self.resultFileEntry = Entry(frame, bd=1, text='', grid=(srow,1), width=50) self.setZipFileName() button = Button(frame, text='Choose File', bd=1, sticky='ew', command=self.chooseZipFile, grid=(srow, 2)) srow +=1 label = Label(frame, text='Results URL:', grid=(srow, 0)) self.resultUrlEntry = Entry(frame, bd=1, text='', grid=(srow,1), width=50) button = Button(frame, text='View Results HTML', bd=1, sticky='ew', command=self.viewHtmlResults, grid=(srow, 2)) srow +=1 texts = ['Submit Project!', 'Check Run Status', 'Purge Server Result', 'Download Results'] commands = [self.runCingServer, self.checkStatus, self.purgeCingServer, self.downloadResults] self.buttonBar = ButtonList(frame, texts=texts, commands=commands, grid=(srow, 0), gridSpan=(1,3)) for button in self.buttonBar.buttons[:1]: button.config(bg=CING_BLUE) # # # # # # Residue frame # # # # # # frame = LabelFrame(tab, text='Residue Options', grid=(1,0)) frame.expandGrid(1,1) label = Label(frame, text='Chain: ') label.grid(row=0,column=0,sticky='w') self.chainPulldown = PulldownList(frame, callback=self.changeChain) self.chainPulldown.grid(row=0,column=1,sticky='w') headingList = ['#','Residue','Linking','Decriptor','Use?'] editWidgets = [None,None,None,None,None] editGetCallbacks = [None,None,None,None,self.toggleResidue] editSetCallbacks = [None,None,None,None,None,] self.residueMatrix = ScrolledMatrix(frame, headingList=headingList, multiSelect=True, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, callback=self.selectResidue) self.residueMatrix.grid(row=1, column=0, columnspan=2, sticky = 'nsew') texts = ['Activate Selected','Inactivate Selected'] commands = [self.activateResidues, self.deactivateResidues] self.resButtons = ButtonList(frame, texts=texts, commands=commands,) self.resButtons.grid(row=2, column=0, columnspan=2, sticky='ew') """ # # # # # # Validate frame # # # # # # frame = LabelFrame(tab, text='Validation Options', grid=(2,0)) frame.expandGrid(None,2) srow = 0 self.selectCheckAssign = CheckButton(frame) self.selectCheckAssign.grid(row=srow, column=0,sticky='nw' ) self.selectCheckAssign.set(True) label = Label(frame, text='Assignments and shifts') label.grid(row=srow,column=1,sticky='nw') srow += 1 self.selectCheckResraint = CheckButton(frame) self.selectCheckResraint.grid(row=srow, column=0,sticky='nw' ) self.selectCheckResraint.set(True) label = Label(frame, text='Restraints') label.grid(row=srow,column=1,sticky='nw') srow += 1 self.selectCheckQueen = CheckButton(frame) self.selectCheckQueen.grid(row=srow, column=0,sticky='nw' ) self.selectCheckQueen.set(False) label = Label(frame, text='QUEEN') label.grid(row=srow,column=1,sticky='nw') srow += 1 self.selectCheckScript = CheckButton(frame) self.selectCheckScript.grid(row=srow, column=0,sticky='nw' ) self.selectCheckScript.set(False) label = Label(frame, text='User Python script\n(overriding option)') label.grid(row=srow,column=1,sticky='nw') self.validScriptEntry = Entry(frame, bd=1, text='') self.validScriptEntry.grid(row=srow,column=2,sticky='ew') scriptButton = Button(frame, bd=1, command=self.chooseValidScript, text='Browse') scriptButton.grid(row=srow,column=3,sticky='ew') """ # # # # # # # # # # self.update(simStore) self.administerNotifiers(application.registerNotify) def downloadResults(self): if not self.run: msg = 'No current iCing run' showWarning('Failure', msg, parent=self) return credentials = self.serverCredentials if not credentials: msg = 'No current iCing server job' showWarning('Failure', msg, parent=self) return fileName = self.resultFileEntry.get() if not fileName: msg = 'No save file specified' showWarning('Failure', msg, parent=self) return if os.path.exists(fileName): msg = 'File %s already exists. Overwite?' % fileName if not showOkCancel('Query', msg, parent=self): return url = self.iCingBaseUrl iCingUrl = self.getServerUrl(url) logText = iCingRobot.iCingFetch(credentials, url, iCingUrl, fileName) print logText msg = 'Results saved to file %s\n' % fileName msg += 'Purge results from iCing server?' if showYesNo('Query',msg, parent=self): self.purgeCingServer() def getServerUrl(self, baseUrl): iCingUrl = os.path.join(baseUrl, 'icing/serv/iCingServlet') return iCingUrl def viewHtmlResults(self): resultsUrl = self.resultsUrl if not resultsUrl: msg = 'No current iCing results URL' showWarning('Failure', msg, parent=self) return webBrowser = WebBrowser(self.application, popup=self.application) webBrowser.open(self.resultsUrl) def runCingServer(self): if not self.project: return run = self.run if not run: msg = 'No CING run setup' showWarning('Failure', msg, parent=self) return structure = self.structure if not structure: msg = 'No structure ensemble selected' showWarning('Failure', msg, parent=self) return ensembleText = getModelsString(run) if not ensembleText: msg = 'No structural models selected from ensemble' showWarning('Failure', msg, parent=self) return residueText = getResiduesString(structure) if not residueText: msg = 'No active residues selected in structure' showWarning('Failure', msg, parent=self) return url = self.iCingBaseUrlPulldown.getObject() url.strip() if not url: msg = 'No iCing server URL specified' showWarning('Failure', msg, parent=self) self.iCingBaseUrl = None return msg = 'Submit job now? You will be informed when the job is done.' if not showOkCancel('Confirm', msg, parent=self): return self.iCingBaseUrl = url iCingUrl = self.getServerUrl(url) self.serverCredentials, results, tarFileName = iCingRobot.iCingSetup(self.project, userId='ccpnAp', url=iCingUrl) if not results: # Message already issued on failure self.serverCredentials = None self.resultsUrl = None self.update() return else: credentials = self.serverCredentials os.unlink(tarFileName) entryId = iCingRobot.iCingProjectName(credentials, iCingUrl).get(iCingRobot.RESPONSE_RESULT) baseUrl, htmlUrl, logUrl, zipUrl = iCingRobot.getResultUrls(credentials, entryId, url) self.resultsUrl = htmlUrl # Save server data in this run for persistence setRunParameter(run, iCingRobot.FORM_USER_ID, self.serverCredentials[0][1]) setRunParameter(run, iCingRobot.FORM_ACCESS_KEY, self.serverCredentials[1][1]) setRunParameter(run, ICING_BASE_URL, url) setRunParameter(run, HTML_RESULTS_URL, htmlUrl) self.update() run.inputStructures = structure.sortedModels() # select residues from the structure's chain #iCingRobot.iCingResidueSelection(credentials, iCingUrl, residueText) # Select models from ensemble #iCingRobot.iCingEnsembleSelection(credentials, iCingUrl, ensembleText) # Start the actual run self.serverDone = False iCingRobot.iCingRun(credentials, iCingUrl) # Fetch server progress occasionally, report when done # this function will call itself again and again self.after(CHECK_INTERVAL, self.timedCheckStatus) self.update() def timedCheckStatus(self): if not self.serverCredentials: return if self.serverDone: return status = iCingRobot.iCingStatus(self.serverCredentials, self.getServerUrl(self.iCingBaseUrl)) if not status: #something broke, already warned return result = status.get(iCingRobot.RESPONSE_RESULT) if result == iCingRobot.RESPONSE_DONE: self.serverDone = True msg = 'CING run is complete!' showInfo('Completion', msg, parent=self) return self.after(CHECK_INTERVAL, self.timedCheckStatus) def checkStatus(self): if not self.serverCredentials: return status = iCingRobot.iCingStatus(self.serverCredentials, self.getServerUrl(self.iCingBaseUrl)) if not status: #something broke, already warned return result = status.get(iCingRobot.RESPONSE_RESULT) if result == iCingRobot.RESPONSE_DONE: msg = 'CING run is complete!' showInfo('Completion', msg, parent=self) self.serverDone = True return else: msg = 'CING job is not done.' showInfo('Processing', msg, parent=self) self.serverDone = False return def purgeCingServer(self): if not self.project: return if not self.run: msg = 'No CING run setup' showWarning('Failure', msg, parent=self) return if not self.serverCredentials: msg = 'No current iCing server job' showWarning('Failure', msg, parent=self) return url = self.iCingBaseUrl results = iCingRobot.iCingPurge(self.serverCredentials, self.getServerUrl(url)) if results: showInfo('Info','iCing server results cleared') self.serverCredentials = None self.iCingBaseUrl = None self.serverDone = False deleteRunParameter(self.run, iCingRobot.FORM_USER_ID) deleteRunParameter(self.run, iCingRobot.FORM_ACCESS_KEY) deleteRunParameter(self.run, HTML_RESULTS_URL) else: showInfo('Info','Purge failed') self.update() def chooseZipFile(self): fileTypes = [ FileType('Zip', ['*.zip']), ] popup = FileSelectPopup(self, file_types=fileTypes, file=self.resultFileEntry.get(), title='Results zip file location', dismiss_text='Cancel', selected_file_must_exist=False) fileName = popup.getFile() if fileName: self.resultFileEntry.set(fileName) popup.destroy() def setZipFileName(self): zipFile = '%s_CING_report.zip' % self.project.name self.resultFileEntry.set(zipFile) def selectStructModel(self, model, row, col): self.model = model def selectResidue(self, residue, row, col): self.residue = residue def deactivateResidues(self): for residue in self.residueMatrix.currentObjects: residue.useInCing = False self.updateResidues() def activateResidues(self): for residue in self.residueMatrix.currentObjects: residue.useInCing = True self.updateResidues() def activateModels(self): if self.run: for model in self.modelTable.currentObjects: if model not in self.run.inputStructures: self.run.addInputStructure(model) self.updateModels() def disableModels(self): if self.run: for model in self.modelTable.currentObjects: if model in self.run.inputStructures: self.run.removeInputStructure(model) self.updateModels() def toggleModel(self, *opt): if self.model and self.run: if self.model in self.run.inputStructures: self.run.removeInputStructure(self.model) else: self.run.addInputStructure(self.model) self.updateModels() def toggleResidue(self, *opt): if self.residue: self.residue.useInCing = not self.residue.useInCing self.updateResidues() def updateResidues(self): if self.residue and (self.residue.topObject is not self.structure): self.residue = None textMatrix = [] objectList = [] colorMatrix = [] if self.chain: chainCode = self.chain.code for residue in self.chain.sortedResidues(): msResidue = residue.residue if not hasattr(residue, 'useInCing'): residue.useInCing = True if residue.useInCing: colors = [None, None, None, None, CING_BLUE] use = 'Yes' else: colors = [None, None, None, None, None] use = 'No' datum = [residue.seqCode, msResidue.ccpCode, msResidue.linking, msResidue.descriptor, use,] textMatrix.append(datum) objectList.append(residue) colorMatrix.append(colors) self.residueMatrix.update(objectList=objectList, textMatrix=textMatrix, colorMatrix=colorMatrix) def updateChains(self): index = 0 names = [] chains = [] chain = self.chain if self.structure: chains = self.structure.sortedCoordChains() names = [chain.code for chain in chains] if chains: if chain not in chains: chain = chains[0] index = chains.index(chain) self.changeChain(chain) self.chainPulldown.setup(names, chains, index) def updateStructures(self): index = 0 names = [] structures = [] structure = self.structure if self.run: model = self.run.findFirstInputStructure() if model: structure = model.structureEnsemble structures0 = [(s.ensembleId, s) for s in self.project.structureEnsembles] structures0.sort() for eId, structure in structures0: name = '%s:%s' % (structure.molSystem.code, eId) structures.append(structure) names.append(name) if structures: if structure not in structures: structure = structures[-1] index = structures.index(structure) self.changeStructure(structure) self.structurePulldown.setup(names, structures, index) def updateModels(self): textMatrix = [] objectList = [] colorMatrix = [] if self.structure and self.run: used = self.run.inputStructures for model in self.structure.sortedModels(): if model in used: colors = [None, CING_BLUE] use = 'Yes' else: colors = [None, None] use = 'No' datum = [model.serial,use] textMatrix.append(datum) objectList.append(model) colorMatrix.append(colors) self.modelTable.update(objectList=objectList, textMatrix=textMatrix, colorMatrix=colorMatrix) def changeStructure(self, structure): if self.project and (self.structure is not structure): self.project.currentEstructureEnsemble = structure self.structure = structure if self.run: self.run.inputStructures = structure.sortedModels() self.updateModels() self.updateChains() def changeChain(self, chain): if self.project and (self.chain is not chain): self.chain = chain self.updateResidues() def chooseValidScript(self): # Prepend default Cyana file extension below fileTypes = [ FileType('Python', ['*.py']), ] popup = FileSelectPopup(self, file_types = fileTypes, title='Python file', dismiss_text='Cancel', selected_file_must_exist = True) fileName = popup.getFile() self.validScriptEntry.set(fileName) popup.destroy() def updateAll(self, project=None): if project: self.project = project self.nmrProject = project.currentNmrProject simStore = project.findFirstNmrSimStore(application='CING') or \ project.newNmrSimStore(application='CING', name='CING') else: simStore = None if not self.project: return self.setZipFileName() if not self.project.currentNmrProject: name = self.project.name self.nmrProject = self.project.newNmrProject(name=name) else: self.nmrProject = self.project.currentNmrProject self.update(simStore) def update(self, simStore=None): NmrSimRunFrame.update(self, simStore) run = self.run urls = [DEFAULT_URL,] index = 0 if run: userId = getRunParameter(run, iCingRobot.FORM_USER_ID) accessKey = getRunParameter(run, iCingRobot.FORM_ACCESS_KEY) if userId and accessKey: self.serverCredentials = [(iCingRobot.FORM_USER_ID, userId), (iCingRobot.FORM_ACCESS_KEY, accessKey)] url = getRunParameter(run, ICING_BASE_URL) if url: htmlUrl = getRunParameter(run, HTML_RESULTS_URL) self.iCingBaseUrl = url self.resultsUrl = htmlUrl # May be None self.resultUrlEntry.set(self.resultsUrl) if self.iCingBaseUrl and self.iCingBaseUrl not in urls: index = len(urls) urls.append(self.iCingBaseUrl) self.iCingBaseUrlPulldown.setup(urls, urls, index) self.updateButtons() self.updateStructures() self.updateModels() self.updateChains() def updateButtons(self, event=None): buttons = self.buttonBar.buttons if self.project and self.run: buttons[0].enable() if self.resultsUrl and self.serverCredentials: buttons[1].enable() buttons[2].enable() buttons[3].enable() else: buttons[1].disable() buttons[2].disable() buttons[3].disable() else: buttons[0].disable() buttons[1].disable() buttons[2].disable() buttons[3].disable()
class IsotopeSchemeEditor(BasePopup): """ **Create and Edit Per-residue Reference Isotope Schemes** This system allows the user to create schemes that describe particular patterns of atomic isotope labelling in terms of combinations of isotopically labelled forms of residues. Once constructed, these schemes may then be applied to a molecule of known residue sequence to gauge the levels of spin-active isotope incorporation in an NMR experiment. This information is useful in several places withing Analysis, including giving more intelligent assignment options and in the generation of distance restraints by matching peak positions to chemical shifts. Although the schemes may be used directly they are typically used as reference information for configuring the `Isotope Labelling`_ system; where isotope labelling patterns are tied to particular molecules and experiments. Because all of the different isotope labelled versions (isotopomers) of each residue type are described independently, a scheme can be used to estimate the specific amounts of incorporation present at multiple atom sites at the same time. For example, although a residue type may have significant levels of 13C at the CA and CB positions on average, there may be no form of the residue where CA and CB are labelled at the same time, and thus CA-CB correlations would not be observed in NMR. This popup window is divided into three main tabs, the first describes the overall schemes that are available; that would be applied to a molecule in a given situation. The second tab details the residue isotopomer components within the selected scheme, i.e. which labelled residue forms are present. The last tab displays isotopomer labelling in a graphical, three-dimensional way. If any isotope labelling schemes have been created or edited the user may immediately save these to disk via the [Save Schemes] button to the right of the tabs, although these will naturally be saved when the main CCPN project is. **Reference Schemes** This table lists all of the reference isotope schemes that are available to the project. A number of standard schemes are included by default, as part of the main CCPN installation. However, the user is free to create new schemes, either from a completely blank description or by copying and modifying one of the existing schemes. By selecting on a isttope scheme row in the table the scheme is selected to be active for the whole popup and the user can see the contents of the scheme via the other two tabs. It should be noted that the user cannot edit the standard schemes provided by CCPN, given that these are stored with the software. Any new or copied schemes that the user creates will be stored inside the current CCPN project. If a new scheme should be made available to multiple projects, its XML file can be copied into the main CCPN installation, if the user has appropriate write access. **Isotopomers** The middle tab allows the user to view, and where appropriate edit, the isotope labelling descriptions for the residues within the current scheme (selected in the pulldown menu at the top). An isotope scheme is constructed by specifying one or more isotopomers for each residue type. Each isotopomer represents a different way of incorporating spin-active atom labels into a given kind of residue. Often there will only be one labelled form of a residue, and hence one isotopomer. However, with some kinds of isotope enrichment, for example using glycerol 13C labelled at the C2 position, connected labelled and unlabelled atom sites can be incorporated in alternative ways, resulting in distinct forms of labelling patterns that are not the result of a pure random mix. Knowing which labels are present at the same time, in the same isotopomer form, can be very important for determining which NMR correlations are possible. In general use when looking through the default, immutable reference schemes that come with CCPN the user can scroll through the isotopomer versions of each residue in the upper table. By clicking on one of these rows the lower table is filled with details of the amount of each kind of isotope (on average) at each atom site. For the lower "Atom Labels" table only one kind of chemical element is shown at a time, but the user may switch to a different one via the "Chemical Element" pulldown. **Editing Isotopomers** When dealing with copied or new isotope schemes the user is allowed to edit all aspects of the scheme. With a completely new scheme there will be no isotopomer records to start with and it is common practice to fill in a standard set of isotopomers, one for each residue type, made with a base level of isotope incorporation. To set this base level the user can use [Set Default Abundances] to manually specify values, although the default is to use natural abundance levels, which is appropriate in most circumstances. With the base levels set the [Add Default Abundance Set] will automatically fill-in a starting set of isotopomers for the scheme. Extra isotopomers can be added for a specific type of residue via the [Add New:] function and adjacent pulldown menu or by copying existing ones; whichever is easier. Each isotopomer has an editable weight to enable the user to indicate the relative abundance within a given residue type. Once a new isotopomer specification is created clicking on its row allows the user to specify the isotope labelling pattern in the lower "Atom Labels" table. Here the user selects which kind of chemical element to consider and then double-clicks to edit the "Weighting" columns in the table. The weightings represent the relative abundance of a given nuclear isotope at a given atom site. The weightings could be set as ratios, fractions, or percentages; it is only the relative proportion that is important. For example if a carbon atom site was known to have 5% Carbon-12 and 95% Carbon-13 isotopes then the respective weights could be entered as 1 & 19 or 0.05 & 0.95; whatever is most convenient. For efficient setup of schemes the [Propagate Abundances] function can be used to spread the same levels of incorporation over several atom sites (from the last selected row). **Isotopomer Structure** The last tab is an alternative way of presenting the isotope patterns present within the residues of the current scheme (selected in either of the first two tabs). Here the user selects a residue type in the upper left pulldown menu and then a numbered isotopomer, or an average of all isotopomers, in the right hand pulldown menu. The structural display will show a moveable picture of the residue (in a standard conformation) where unlabelled atom sites are represented with grey spheres, labelled sites with yellow spheres and intermediate incorporation with shades in between. It should be noted that this kind of 3D display is only possible if there is an idealised structure available for a residue type. This data will be present for all of the regular biopolymer residues, but may be missing for more unusual compounds; although a lack of coordinates does not impact upon the isotopomer setup. To move and rotate the three-dimensional residue display the following keyboard controls may be used: * Rotate: Arrow keys * Zoom: Page Up & Page Down keys * Translate: Arrow keys + Control key Or alternatively the following mouse controls: * Rotate: Middle button click & drag * Zoom: Mouse wheel or middle button click + Shift key & drag up/down * Translate: Middle button click & drag + Control key Also an options menu appears when the right mouse button is clicked. .. _`Isotope Labelling`: EditMolLabellingPopup.html """ def __init__(self, parent, project=None, *args, **kw): if not project: self.project = Implementation.MemopsRoot( name='defaultUtilityProject') else: self.project = project self.waiting = False self.waitingAtom = False self.molType = 'protein' self.scheme = None self.isotopomer = None self.isotopomerV = False # Not None self.ccpCodeV = None self.element = 'C' self.atomLabelTuple = None self.isotopes = [x[0] for x in getSortedIsotopes(self.project, 'C')] self.defaultAbun = {} BasePopup.__init__(self, parent=parent, title='Molecule : Reference Isotope Schemes', **kw) def body(self, guiFrame): self.geometry('700x600') guiFrame.expandGrid(0, 0) tipTexts = [ 'A table of all of the reference isotope scheme definitions available to the project', 'A list of the residue isotopomers that comprise the selected isotope labelling scheme', 'A three-dimensional representation of residues and their isotopomer labelling' ] options = ['Reference Schemes', 'Isotopomers', 'Isotopomer Structure'] tabbedFrame = TabbedFrame(guiFrame, options=options, grid=(0, 0), tipTexts=tipTexts) self.tabbedFrame = tabbedFrame frameA, frameB, frameC = tabbedFrame.frames # # Schemes # frameA.expandGrid(0, 0) tipTexts = [ 'A short textual code that identifies the reference isotope scheme in graphical displays', 'The full name for the isotope scheme', 'A detailed description of the isotope scheme including user comments', 'The name of the CCPN data repository in which the isotope scheme is saved; "refData" is in the CCPn installation' ] headingList = ['Code', 'Name', 'Description', 'Save Location'] self.schemeNameEntry = Entry(self, text='', returnCallback=self.setSchemeName, width=20) self.schemeDetailsEntry = Entry(self, text='', returnCallback=self.setSchemeDetails, width=20) editWidgets = [ None, self.schemeNameEntry, self.schemeDetailsEntry, None ] editGetCallbacks = [ None, self.getSchemeName, self.getSchemeDetails, None ] editSetCallbacks = [ None, self.setSchemeName, self.setSchemeDetails, None ] self.schemeMatrix = ScrolledMatrix(frameA, headingList=headingList, callback=self.selectScheme, editWidgets=editWidgets, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, multiSelect=False, grid=(0, 0), tipTexts=tipTexts) self.schemeMatrix.doEditMarkExtraRules = self.schemeEditRules tipTexts = [ 'Make a new reference isotope scheme definition based on a copy of the scheme currently selected', 'Delete the selected isotope scheme', 'Make a new, blank isotope scheme' ] texts = ['Copy', 'Delete', 'New'] commands = [self.copyScheme, self.removeScheme, self.makeNewScheme] self.schemeButtons = ButtonList(frameA, texts=texts, commands=commands, grid=(1, 0), tipTexts=tipTexts) # # Isotopomers # frameB.expandGrid(3, 0) row = 0 frame = Frame(frameB, grid=(row, 0)) frame.expandGrid(0, 2) tipText = 'Selects which of the available isotope schemes to view/edit' label = Label(frame, text='Reference Scheme:', grid=(0, 0)) self.schemePulldown = PulldownList(frame, callback=self.setLabellingScheme, grid=(0, 1), tipText=tipText) row += 1 div = LabelDivider(frameB, text='Isotopomers', grid=(row, 0)) row += 1 frame = Frame(frameB, grid=(row, 0)) frame.expandGrid(1, 2) self.isotopomerFrame = frame self.abundanceWidget = MultiWidget(self, FloatEntry, relief='raised', borderwidth=2, callback=self.setDefaultAbundances, useImages=False) tipText = 'Opens a panel that allows you to set the basis/default abundances for C, H & N isotopes; used as the starting point for new isotopomer definitions' self.abundanceButton = Button(frame, text='Set Default\nAbundances', borderwidth=1, command=self.enterDefaultAbundances, grid=(0, 0), tipText=tipText) tipText = 'Sets the basis/default abundances for C, H & N isotopes to their natural abundance proportions' button = Button(frame, text='Set Natural\nAbundance Default', borderwidth=1, command=self.resetDefaultAbundance, grid=(0, 1), sticky='ew', tipText=tipText) label = Label(frame, text='Molecule Type:', grid=(0, 2), sticky='e') entries = standardResidueCcpCodes.keys() entries.sort() entries.reverse() tipText = 'Selects which type of bio-polymer to define residue isotopomer labelling for' self.molTypePulldown = PulldownList(frame, callback=self.setMolType, texts=entries, grid=(0, 3), tipText=tipText) row += 1 tipTexts = [ 'The CCPN code that identifies the kind of residue the isotopomer relates to', 'The number of the particular isotopomer (isotope pattern) within its residue type', 'The fraction of the total residues, of its kind, that the isotopomer make up' ] headingList = ['Ccp Code', 'Variant', 'Weight'] self.isotopomerWeightEntry = FloatEntry( self, text='', returnCallback=self.setIsotopomerWeight, width=6) editWidgets = [None, None, self.isotopomerWeightEntry] editGetCallbacks = [None, None, self.getIsotopomerWeight] editSetCallbacks = [None, None, self.setIsotopomerWeight] self.isotopomerMatrix = ScrolledMatrix( frameB, tipTexts=tipTexts, headingList=headingList, callback=self.selectIsotopomer, editWidgets=editWidgets, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, multiSelect=True, grid=(row, 0)) self.isotopomerMatrix.doEditMarkExtraRules = self.isotopomerEditRules row += 1 frame = Frame(frameB, grid=(row, 0), sticky='ew') frame.expandGrid(0, 0) tipTexts = [ 'Delete the selected residue isotopomers from the current isotope scheme', 'Make a new residue isotopomer definition by copying the details of the last selected isotopomer', 'Add a complete set of isotopomers to the isotope scheme, one for each residue type, based on the states default isotope abundances', 'For all residue isotopomers in the scheme, set the labelling of one kind of atom (the user is prompted) to its default isotopic incorporation ', 'Add a new residue isotopomer definition that uses the default isotopic incorporation' ] texts = [ 'Delete\nSelected', 'Copy\nSelected', 'Add Default\nAbundance Set', 'Set Atom Type\nTo Default', 'Add\nNew:' ] commands = [ self.removeIsotopomers, self.duplicateResidues, self.addDefaultIsotopomers, self.setAtomTypeDefault, self.addNewIsotopomer ] self.isotopomerButtons = ButtonList(frame, texts=texts, commands=commands, grid=(0, 0), tipTexts=tipTexts) tipText = 'Selects which kind of residue isotopomer may be added to the current isotope scheme' self.ccpCodePulldown = PulldownList(frame, callback=None, grid=(0, 1), sticky='e', tipText=tipText) row += 1 div = LabelDivider(frameB, text='Atom Labels', grid=(row, 0)) row += 1 frame = Frame(frameB, grid=(row, 0)) frame.expandGrid(1, 3) label = Label(frame, text='Chemical Element:', grid=(0, 0)) tipText = 'Selects which kind of atoms to select from the selected residue isotopomer; to display isotopic incorporation in the below table' self.elementPulldown = PulldownList(frame, callback=self.changeChemElement, grid=(0, 1), tipText=tipText) self.updateChemElements() label = Label(frame, text='Water Exchangeable Atoms:', grid=(0, 2)) tipText = 'Sets whether to show atoms considered as being "water exchangeable"; their isotopic labelling will rapidly equilibrate with aqueous solvent' self.exchangeCheck = CheckButton(frame, callback=self.updateAtomLabelsAfter, grid=(0, 3), selected=False, tipText=tipText) row += 1 # Tip texts set on update headingList = [ 'Atom\nName', 'Weighting\n13C' 'Weighting\n12C', '%12C', '%13C' ] self.atomLabelTupleWeightEntry = FloatEntry( self, text='', width=6, returnCallback=self.setAtomLabelWeight) self.atomsMatrix = ScrolledMatrix(frameB, headingList=headingList, callback=self.selectAtomLabel, multiSelect=True, grid=(row, 0)) self.atomsMatrix.doEditMarkExtraRules = self.atomsEditRules row += 1 tipTexts = [ 'For the selected atom sites, in the current isotopomer, set their isotopic incorporation to the default values', 'Spread the isotopic incorporation values from the last selected atom site to all selected atoms sites' ] texts = ['Reset Selected to Default Abundance', 'Propagate Abundances'] commands = [self.setAtomLabelsDefault, self.propagateAbundances] self.atomButtons = ButtonList(frameB, texts=texts, commands=commands, grid=(row, 0), tipTexts=tipTexts) # # View Frame # frameC.expandGrid(1, 0) row = 0 frame = Frame(frameC, grid=(row, 0), sticky='ew') frame.grid_columnconfigure(3, weight=1) label = Label(frame, text='Residue Type:', grid=(0, 0)) tipText = 'Selects which kind of residue, within the current isotope scheme, to show isotopomer structures for' self.viewCcpCodePulldown = PulldownList( frame, callback=self.selectViewCcpcode, grid=(0, 1), tipText=tipText) label = Label(frame, text='Isotopomer:', grid=(0, 2)) tipText = 'Selects which kind of isotopomer (labelling pattern) to display, from the selected residue type.' self.viewIsotopomerPulldown = PulldownList( frame, callback=self.selectViewIsotopomer, grid=(0, 3), tipText=tipText) row += 1 self.viewIsotopomerFrame = ViewIsotopomerFrame(frameC, None, grid=(row, 0)) # # Main # tipTexts = [ 'Save all changes to the reference isotope scheme to disk; the saves ALL changes to the CCPN installation for all projects to use', ] texts = ['Save Schemes'] commands = [self.saveSchemes] self.bottomButtons = UtilityButtonList(tabbedFrame.sideFrame, texts=texts, commands=commands, helpUrl=self.help_url, grid=(0, 0), sticky='e', tipTexts=tipTexts) self.updateChemElements() self.updateCcpCodes() self.updateSchemes() self.administerNotifiers(self.registerNotify) def atomsEditRules(self, atomLabel, row, col): if self.scheme: return isSchemeEditable(self.scheme) else: return False def isotopomerEditRules(self, isotopomer, row, col): if self.scheme: return isSchemeEditable(self.scheme) else: return False def schemeEditRules(self, scheme, row, col): return isSchemeEditable(scheme) def administerNotifiers(self, notifyFunc): for func in ('__init__', 'delete', 'setLongName', 'setDetails'): for clazz in ('ccp.molecule.ChemCompLabel.LabelingScheme', ): notifyFunc(self.updateSchemes, clazz, func) for func in ('__init__', 'delete', 'setWeight'): notifyFunc(self.updateIsotopomersAfter, 'ccp.molecule.ChemCompLabel.Isotopomer', func) notifyFunc(self.updateAtomLabelsAfter, 'ccp.molecule.ChemCompLabel.AtomLabel', func) def getCcpCodeIsotopomers(self, ccpCode): chemCompLabel = self.scheme.findFirstChemCompLabel( molType=self.molType, ccpCode=ccpCode) if chemCompLabel: isotopomers = list(chemCompLabel.isotopomers) else: isotopomers = [] return isotopomers def selectViewCcpcode(self, ccpCode): if ccpCode != self.ccpCodeV: self.ccpCodeV = ccpCode self.isotopomerV = False self.updateViewIsotopomerPulldown() def selectViewIsotopomer(self, isotopomer): self.isotopomerV = isotopomer if isotopomer is None: isotopomers = self.getCcpCodeIsotopomers(self.ccpCodeV) else: isotopomers = [ isotopomer, ] self.viewIsotopomerFrame.setIsotopomers(isotopomers) def updateViewCcpCodePulldown(self): if self.scheme: codes = self.getCcpCodes(self.molType) if self.ccpCodeV not in codes: self.ccpCodeV = codes[0] self.isotopomerV = False # Not None self.updateViewIsotopomerPulldown() index = codes.index(self.ccpCodeV) else: codes = [] index = 0 self.viewCcpCodePulldown.setup(codes, codes, index) def updateViewIsotopomerPulldown(self): index = 0 isotopomers = [] names = [] if self.scheme: isotopomers = self.getCcpCodeIsotopomers(self.ccpCodeV) names = ['%d' % i.serial for i in isotopomers] isotopomers.insert(0, None) names.insert(0, '<All>') if self.isotopomerV not in isotopomers: self.isotopomerV = None isotopomers = self.getCcpCodeIsotopomers(self.ccpCodeV) self.viewIsotopomerFrame.setIsotopomers(isotopomers) self.viewIsotopomerPulldown.setup(names, isotopomers, index) def updateButtons(self): buttonsA = self.schemeButtons.buttons buttonsB = self.isotopomerButtons.buttons buttonsC = self.atomButtons.buttons if self.scheme: buttonsA[0].enable() isEditable = isSchemeEditable(self.scheme) if isEditable: buttonsA[1].enable() else: buttonsA[1].disable() buttonsB[2].enable() buttonsB[3].enable() self.bottomButtons.buttons[0].enable() if isEditable: if self.isotopomer: for button in buttonsB: button.enable() for button in buttonsC: button.enable() else: buttonsB[0].disable() buttonsB[1].disable() buttonsB[3].disable() buttonsC[0].disable() buttonsC[1].disable() buttonsB[2].enable() buttonsB[4].enable() else: for button in buttonsB: button.disable() for button in buttonsC: button.disable() else: buttonsA[0].disable() buttonsA[1].disable() for button in buttonsB: button.disable() for button in buttonsC: button.disable() self.bottomButtons.buttons[0].disable() def resetDefaultAbundance(self): self.defaultAbun = {} def setDefaultAbundances(self, values): self.abundanceWidget.place_forget() if values is not None: i = 0 for element in elementSymbols: # TBD getAllIsotopes for code, isotope in getSortedIsotopes(self.project, element): self.defaultAbun[isotope] = values[i] i += 1 def enterDefaultAbundances(self): x = self.isotopomerFrame.winfo_x() y = self.isotopomerFrame.winfo_y() x0 = self.abundanceButton.winfo_x() y0 = self.abundanceButton.winfo_y() values = [] options = [] for element in elementSymbols: # TBD getAllIsotopes for code, isotope in getSortedIsotopes(self.project, element): options.append(code + ':') values.append( self.defaultAbun.get(isotope, 100.0 * isotope.abundance)) N = len(values) self.abundanceWidget.maxRows = N self.abundanceWidget.minRows = N self.abundanceWidget.set(values=values, options=options) self.abundanceWidget.place(x=x + x0, y=y + y0) def selectAtomLabel(self, obj, row, col): self.atomLabelTuple = (obj, col) def setMolType(self, molType): if molType != self.molType: self.molType = molType self.isotopomer = None self.updateCcpCodes() self.updateIsotopomers() def getCcpCodes(self, molType): codes = [] for code in standardResidueCcpCodes[molType]: codes.append(code) codes.sort() return codes def updateCcpCodes(self): codes = self.getCcpCodes(self.molType) if self.isotopomer: index = codes.index(self.isotopomer.ccpCode) else: index = 0 self.ccpCodePulldown.setup(codes, codes, index) def setIsotopomerWeight(self, event): value = self.isotopomerWeightEntry.get() if value is not None: self.isotopomer.setWeight(abs(value)) def getIsotopomerWeight(self, isotopomer): if isotopomer: self.isotopomerWeightEntry.set(isotopomer.weight) def setSchemeName(self, event): text = self.schemeNameEntry.get() if text: text = text.strip() or None else: text = None self.scheme.setLongName(text) def getSchemeName(self, scheme): if scheme: self.schemeNameEntry.set(scheme.longName) def getSchemeDetails(self, scheme): if scheme: self.schemeDetailsEntry.set(scheme.details) def setSchemeDetails(self, event): text = self.schemeDetailsEntry.get() if text: text = text.strip() or None else: text = None self.scheme.setDetails(text) def updateSchemes(self, obj=None): textMatrix = [] objectList = [] for labelingScheme in self.project.sortedLabelingSchemes(): repository = labelingScheme.findFirstActiveRepository() if repository: saveLocation = repository.name else: saveLocation = None line = [ labelingScheme.name, labelingScheme.longName, labelingScheme.details, saveLocation ] textMatrix.append(line) objectList.append(labelingScheme) self.schemeMatrix.update(textMatrix=textMatrix, objectList=objectList) self.updateSchemePulldown() self.updateIsotopomers() def updateSchemePulldown(self): scheme = self.scheme schemes = self.project.sortedLabelingSchemes() names = [ls.longName or ls.name for ls in schemes] if names: if scheme not in schemes: scheme = schemes[0] index = schemes.index(scheme) else: index = 0 scheme = None self.setLabellingScheme(scheme) self.schemePulldown.setup(names, schemes, index) def copyScheme(self): if self.scheme: name = askString('Input text', 'New Scheme Code:', '', parent=self) scheme = self.project.findFirstLabelingScheme(name=name) if scheme: showWarning('Failure', 'Scheme name already in use') return if name: newScheme = copySubTree(self.scheme, self.project, topObjectParameters={'name': name}) else: showWarning('Failure', 'No name specified') else: showWarning('Failure', 'No scheme selected to copy') def removeScheme(self): if self.scheme and isSchemeEditable(self.scheme): self.scheme.delete() self.scheme = None def makeNewScheme(self): name = askString('Input text', 'New Scheme Code:', '', parent=self) if name: scheme = self.project.findFirstLabelingScheme(name=name) if scheme: showWarning('Failure', 'Scheme name already in use') else: scheme = self.project.newLabelingScheme(name=name) self.scheme = scheme else: showWarning('Failure', 'No name specified') def setLabellingScheme(self, scheme): if scheme is not self.scheme: self.scheme = scheme self.isotopomerV = False self.isotopomer = None self.updateIsotopomers() def selectScheme(self, object, row, col): self.setLabellingScheme(object) self.updateSchemePulldown() def open(self): BasePopup.open(self) self.updateSchemes() def saveSchemes(self): schemes = [x for x in self.project.labelingSchemes if x.isModified] if schemes: for scheme in schemes: scheme.save() showInfo('Notice', 'Successfully saved %d schemes' % len(schemes)) self.updateSchemes() else: showWarning('Notice', 'No modified schemes to save') def addNewIsotopomer(self): if self.scheme: ccpCode = self.ccpCodePulldown.getObject() chemCompLabel = self.getChemCompLabel(self.molType, ccpCode) self.makeIsotopomer(chemCompLabel) def makeIsotopomer(self, chemCompLabel, weight=1.0): isotopomer = chemCompLabel.newIsotopomer(weight=weight) chemComp = chemCompLabel.chemComp for chemAtom in chemComp.chemAtoms: if chemAtom.elementSymbol: chemElement = chemAtom.chemElement for isotope in chemElement.isotopes: code = '%d%s' % (isotope.massNumber, chemAtom.elementSymbol) weight = self.defaultAbun.get(isotope, 100.0 * isotope.abundance) isotopomer.newAtomLabel(name=chemAtom.name, subType=chemAtom.subType, isotopeCode=code, weight=weight) return isotopomer def getChemCompLabel(self, molType, ccpCode): chemCompLabel = None if self.scheme: chemCompLabel = self.scheme.findFirstChemCompLabel(molType=molType, ccpCode=ccpCode) if not chemCompLabel: chemCompLabel = self.scheme.newChemCompLabel(molType=molType, ccpCode=ccpCode) return chemCompLabel def selectIsotopomer(self, obj, row, col): self.isotopomer = obj self.updateChemElements() self.updateAtomLabels() def updateIsotopomersAfter(self, obj=None): if self.waiting: return else: self.waiting = True self.after_idle(self.updateIsotopomers) def updateIsotopomers(self): self.updateViewCcpCodePulldown() self.updateViewIsotopomerPulldown() textMatrix = [] objectList = [] if self.scheme: chemCompLabels = [(label.ccpCode, label) for label in self.scheme.chemCompLabels] chemCompLabels.sort() for key, chemCompLabel in chemCompLabels: if chemCompLabel.molType == self.molType: for isotopomer in chemCompLabel.sortedIsotopomers(): line = [ chemCompLabel.ccpCode, isotopomer.serial, isotopomer.weight ] textMatrix.append(line) objectList.append(isotopomer) self.isotopomerMatrix.update(textMatrix=textMatrix, objectList=objectList) self.updateAtomLabelsAfter() self.waiting = False def setAtomTypeDefault(self): if self.scheme: atomName = askString( 'Query', 'Specify atom name to set\ndefault abundance for', 'H', parent=self) if not atomName: return atomLabels = [] for chemCompLabel in self.scheme.chemCompLabels: if chemCompLabel.molType == self.molType: for isotopomer in chemCompLabel.isotopomers: # Multiple because of isotopes and subTypes atomLabels += isotopomer.findAllAtomLabels( name=atomName) if atomLabels: for atomLabel in atomLabels: isotope = atomLabel.isotope weight = self.defaultAbun.get(isotope, 100.0 * isotope.abundance) atomLabel.weight = weight else: data = (atomName, self.scheme.name) msg = 'Atom name %s does not match any atoms in %s scheme isotopomers' % data showWarning('Failure', msg) def addDefaultIsotopomers(self): if self.scheme: codes = self.getCcpCodes(self.molType) for ccpCode in codes: chemCompLabel = self.getChemCompLabel(self.molType, ccpCode) if not chemCompLabel.isotopomers: self.makeIsotopomer(chemCompLabel) def removeIsotopomers(self): isotopomers = self.isotopomerMatrix.currentObjects if isotopomers: for isotopomer in isotopomers: isotopomer.delete() self.isotopomer = None def duplicateResidues(self): isotopomers = self.isotopomerMatrix.currentObjects for isotopomer in isotopomers: chemCompLabel = isotopomer.chemCompLabel new = copySubTree(isotopomer, chemCompLabel) def updateChemElements(self): if self.isotopomer: chemCompLabel = self.isotopomer.chemCompLabel elementDict = {} for chemAtom in chemCompLabel.chemComp.chemAtoms: symbol = chemAtom.elementSymbol if symbol: elementDict[symbol] = True names = elementDict.keys() names.sort() else: names = ['C', 'N', 'H'] if self.element not in names: index = 0 else: index = names.index(self.element) self.elementPulldown.setup(names, names, index) def updateAtomLabelsAfter(self, obj=None): if self.waitingAtom: return else: self.waitingAtom = True self.after_idle(self.updateAtomLabels) def updateAtomLabels(self): element = self.elementPulldown.getText() textMatrix = [] objectList = [] headingList = [ 'Atom Name', ] tipTexts = [ 'The name of the atom within its residue, for which to set isotopic abundances, within the selected isotopomer', ] isotopeCodes = [x[0] for x in getSortedIsotopes(self.project, element)] headingList.extend(['Weighting\n%s' % x for x in isotopeCodes]) tip = 'The amount of %s isotope incorporation; can be a ratio, percentage or fraction (the value used is relative to the sum of all weights)' tipTexts.extend([tip % x for x in isotopeCodes]) tip = 'The percentage %s isotope incorporation, calculated using stated weights' headingList.extend(['%%%s' % x for x in isotopeCodes]) tipTexts.extend([tip % x for x in isotopeCodes]) editWidgets = [ None, ] editGetCallbacks = [ None, ] editSetCallbacks = [ None, ] editWidgets.extend( [self.atomLabelTupleWeightEntry for x in isotopeCodes]) editGetCallbacks.extend( [self.getAtomLabelWeight for x in isotopeCodes]) editSetCallbacks.extend( [self.setAtomLabelWeight for x in isotopeCodes]) editWidgets.extend([None for x in isotopeCodes]) editGetCallbacks.extend([None for x in isotopeCodes]) editSetCallbacks.extend([None for x in isotopeCodes]) doExchangeable = self.exchangeCheck.get() if self.isotopomer: atomDict = {} for atomLabel in self.isotopomer.sortedAtomLabels(): if atomLabel.chemAtom.elementSymbol == element: if (not doExchangeable) and ( atomLabel.chemAtom.waterExchangeable): continue name = atomLabel.name subType = atomLabel.subType atomDict[(name, subType)] = True atomNames = atomDict.keys() atomNames = greekSortAtomNames(atomNames) for atomName, subType in atomNames: if subType == 1: name = atomName else: name = '%s:%d' % (atomName, subType) line = [ name, ] atomLabels = [] sumWeights = 0.0 for isotope in isotopeCodes: atomLabel = self.isotopomer.findFirstAtomLabel( name=atomName, subType=subType, isotopeCode=isotope) atomLabels.append(atomLabel) if atomLabel: weight = atomLabel.weight sumWeights += weight line.append(weight) else: line.append(0.0) if sumWeights: for atomLabel in atomLabels: line.append(100.0 * atomLabel.weight / sumWeights) else: for atomLabel in atomLabels: line.append(None) textMatrix.append(line) objectList.append(atomLabels) self.atomsMatrix.update(textMatrix=textMatrix, objectList=objectList, headingList=headingList, tipTexts=tipTexts, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks) self.waitingAtom = False self.updateButtons() def setAtomLabelWeight(self, event): value = self.atomLabelTupleWeightEntry.get() if value is not None: atomLabels, col = self.atomLabelTuple chemAtom = None for label in atomLabels: if label: chemAtom = label.chemAtom break atomLabel = atomLabels[col - 1] if chemAtom and (atomLabel is None): isotopeCode, isotope = getSortedIsotopes( self.project, chemAtom.elementSymbol)[col - 1] atomLabel = self.isotopomer.newAtomLabel( name=chemAtom.name, subType=chemAtom.subType, isotopeCode=isotopeCode, weight=value) atomLabel.setWeight(value) def setAtomLabelsDefault(self): atomLabelTuples = self.atomsMatrix.currentObjects for atomLabels in atomLabelTuples: for atomLabel in atomLabels: isotope = atomLabel.isotope weight = self.defaultAbun.get(isotope, 100.0 * isotope.abundance) atomLabel.weight = weight def propagateAbundances(self): atomLabels, col = self.atomLabelTuple atomLabelTuples = self.atomsMatrix.currentObjects weightDict = {} for atomLabel in atomLabels: weightDict[atomLabel.isotope] = atomLabel.weight for atomLabels0 in atomLabelTuples: if atomLabels0 != atomLabels: for atomLabel in atomLabels0: atomLabel.weight = weightDict[atomLabel.isotope] def getAtomLabelWeight(self, null): atomLabels, col = self.atomLabelTuple if atomLabels and (col > 0): atomLabel = atomLabels[col - 1] if atomLabel is None: weight = 0.0 else: weight = atomLabel.weight self.atomLabelTupleWeightEntry.set(weight) def changeChemElement(self, name): self.element = name self.isotopes = [x[0] for x in getSortedIsotopes(self.project, name)] self.updateAtomLabels() def destroy(self): self.administerNotifiers(self.unregisterNotify) BasePopup.destroy(self)
class CreateContourFilePopup(BasePopup): """ **Create Contour Files for Project** The purpose of this dialog is to allow the user to create new contour files for the project. Not only the spectrum has to be specified, but also the two dimensions (X, Y) that are going to be contoured. The table specifies the conditions that specify the region being contoured, in terms of what is included and excluded. For now, only one condition (which is an "include" condition) is allowed. This also means that the "Add Condition" and "Delete Condition" buttons are disabled. The contour directory is specified by the program, using the project directory but the file name can be specified by the user. The contour levels used are the ones that are current for the spectrum, as set in the `Contour Levels dialog`_. See also: `Spectrum Contour Files`_, `Add Existing Contour Files`_. .. _`Contour Levels dialog`: EditContourLevelsPopup.html .. _`Spectrum Contour Files`: EditContourFilesPopup.html .. _`Add Existing Contour Files`: AddContourFilePopup.html """ def __init__(self, parent, *args, **kw): self.spectrumConditions = [] self.col = None self.spectrum = None BasePopup.__init__(self, parent=parent, title='New Contour File', **kw) def body(self, master): self.geometry('650x200') master.grid_columnconfigure(1, weight=1) row = 0 frame = Frame(master, grid=(row, 0), gridSpan=(1,2)) label = Label(frame, text='Spectrum: ', grid=(row, 0)) tipText = 'Selects the experiment and spectrum for which to make a contour file' self.expt_spectrum = PulldownList(frame, grid=(row, 1), tipText=tipText, callback=self.update) label = Label(frame, text=' (X-dim, Y-dim): ', grid=(row, 2)) tipText = 'Selects which dimensions (projection) of the spectrum form the X and Y axes of the contour file' self.dim_menu = PulldownList(frame, grid=(row, 3), tipText=tipText, callback=self.updateFile) row = row + 1 master.grid_rowconfigure(row, weight=1) #### for now not editable because only allow one include region ###self.conditionMenu = PulldownMenu(self, entries=('include', 'exclude'), ### callback=self.selectedCondition, do_initial_callback=False) self.regionEntry = FloatEntry(self, text='', returnCallback=self.setRegion, width=10) tipTexts = ['Whether to include or exclude the region in the contour file', '', '', ''] headingList = ['Condition','','','',''] editSetCallbacks = [None] editGetCallbacks = [None] ###editWidgets = [self.conditionMenu] editWidgets = [None] self.conditionTable = ScrolledMatrix(master, initialRows=6, grid=(row, 0), gridSpan=(1,2), tipTexts=tipTexts, headingList=headingList, callback=self.selectCell, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks) # TBD: make directory editable row = row + 1 label = Label(master, text='Contour dir: ', grid=(row, 0), sticky='e') tipText = 'The directory location on disk into which contour files are saved' self.dir_label = Label(master, text='', grid=(row, 1), tipText=tipText) row = row + 1 label = Label(master, text='File name: ', grid=(row, 0), sticky='e') tipText = 'Sets the name of the contour file to save to disk' self.file_entry = Entry(master, grid=(row, 1), tipText=tipText) ##row = row + 1 ##label = Label(master, text='Contour levels: ') ##label.grid(row=row, column=0, sticky='e') ##self.levels_entry = FloatEntry(master, isArray=True, returnCallback=self.saveLevels) ##self.levels_entry.grid(row=row, column=1, sticky='ew') row = row + 1 tipTexts = ['Add a new contour region for including in or excluding from the contour file ', 'Remove the selected contour region from the table', 'Make the specified contour file using the input settings & regions'] texts = [ 'Add Condition', 'Delete Condition', 'Contour and Save' ] commands = [ self.addCondition, self.deleteCondition, self.contourAndSaveSpectrum ] self.buttons = UtilityButtonList(master, texts=texts, commands=commands, doClone=False, helpUrl=self.help_url, grid=(row, 0), gridSpan=(1,2), tipTexts=tipTexts) ### TBD disabled for now for n in range(2): self.buttons.buttons[n].config(state='disabled') self.curateNotifiers(self.registerNotify) self.updateSpectrum() self.updateDimMenu() def destroy(self): self.curateNotifiers(self.unregisterNotify) BasePopup.destroy(self) def curateNotifiers(self, notifyFunc): for clazz in ('Experiment', 'DataSource'): for func in ('__init__', 'delete', 'setName'): notifyFunc(self.updateNotifier, 'ccp.nmr.Nmr.%s' % clazz, func) def update(self, spectrum): if spectrum is self.spectrum: return self.spectrum = spectrum self.updateDimMenu() self.updateConditionTable() self.updateFile() ##self.updateLevels() def updateSpectrum(self, spectrum=None): if not spectrum: spectrum = self.spectrum spectra = self.parent.getSpectra() if spectra: if spectrum not in spectra: spectrum = spectra[0] index = spectra.index(spectrum) names = ['%s:%s' % (x.experiment.name, x.name) for x in spectra] else: index = 0 names = [] self.expt_spectrum.setup(names, spectra, index) self.update(spectrum) def updateNotifier(self, *extra): self.updateSpectrum() def updateFile(self, *extra): spectrum = self.spectrum if not spectrum: return analysisSpectrum = spectrum.analysisSpectrum if not analysisSpectrum: return dims = self.dim_menu.getText() dims = [int(dim) for dim in dims.split(', ')] (xdim, ydim) = dims storedContour = analysisSpectrum.findFirstStoredContour(dims=dims) if not storedContour: storedContour = analysisSpectrum.findFirstStoredContour() if storedContour: fileName = storedContour.path else: fileName = '%s_%s_%d_%d.cnt' % (spectrum.experiment.name, spectrum.name, xdim, ydim) path = self.getContourDir() self.dir_label.set(path) self.file_entry.set(fileName) def getContourDir(self): spectrum = self.spectrum if not spectrum: return '' analysisSpectrum = spectrum.analysisSpectrum if not analysisSpectrum: return '' url = analysisSpectrum.contourDir if url: # should be the case since set in Analysis.py path = url.dataLocation else: path = '' return path """ def saveFile(self, *extra): spectrum = self.spectrum if not spectrum: return analysisSpectrum = spectrum.analysisSpectrum if not analysisSpectrum: return fileName = self.file_entry.get() if fileName: application = self.project.application application.setValue(spectrum, keyword='contourFileName', value=fileName) """ def updateDimMenu(self): entries = [] spectrum = self.spectrum if spectrum: ndim = spectrum.numDim for xdim in range(1, ndim): dataDim = spectrum.findFirstDataDim(dim=xdim) if dataDim.className != 'SampledDataDim': for ydim in range(xdim+1, ndim+1): dataDim = spectrum.findFirstDataDim(dim=ydim) if dataDim.className != 'SampledDataDim': entries.append('%d, %d' % (xdim, ydim)) self.dim_menu.setup(entries, objects=None, index=0) def updateConditionTable(self, *extra): spectrum = self.spectrum if spectrum: ndim = spectrum.numDim else: ndim = 0 tipTexts = ['Whether to include or exclude the region in the contour file',] headingList = ['Condition'] ###editWidgets = [self.conditionMenu] + 2*ndim*[self.regionEntry] ###editGetCallbacks = [self.getCondition] ###editSetCallbacks = [self.setCondition] editWidgets = [None] + 2*ndim*[self.regionEntry] editGetCallbacks = [None] editSetCallbacks = [None] for i in range(ndim): dim = i + 1 headingList.extend(['Dim %d Min' % dim, 'Dim %d Max' % dim]) editGetCallbacks.append(lambda obj, i=i: self.getRegionMin(obj, i)) editGetCallbacks.append(lambda obj, i=i: self.getRegionMax(obj, i)) editSetCallbacks.append(lambda obj, i=i: self.setRegionMin(obj, i)) editSetCallbacks.append(lambda obj, i=i: self.setRegionMax(obj, i)) tipTexts.append('Lower bound of dimension %d region to include or exclude' % dim) tipTexts.append('Upper bound of dimension %d region to include or exclude' % dim) objectList = [] textMatrix = [] self.spectrumConditions = self.getSpectrumConditions() for spectrumCondition in self.spectrumConditions: (condition, region) = spectrumCondition objectList.append(spectrumCondition) textRow = [condition] for i in range(ndim): r = region[i] if r: rMin = r[0] rMax = r[1] else: rMin = rMax = None textRow.append(rMin) textRow.append(rMax) textMatrix.append(textRow) self.conditionTable.update(objectList=objectList, textMatrix=textMatrix, headingList=headingList, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets) """ def updateLevels(self): spectrum = self.getSpectrum() if spectrum: analysisSpectrum = spectrum.analysisSpectrum levels = analysisSpectrum.posLevels + analysisSpectrum.negLevels scale = spectrum.scale / self.analysisProject.globalContourScale levels = [ level/scale for level in levels ] else: levels = [] self.levels_entry.set(levels) def saveLevels(self): spectrum = self.spectrum if not spectrum: return """ def getRegionMin(self, spectrumCondition, dim): #print 'getRegionMin' (condition, region) = spectrumCondition self.regionEntry.set(region[dim][0]) def setRegionMin(self, spectrumCondition, dim): #print 'setRegionMin' (condition, region) = spectrumCondition (r0, r1) = region[dim] r = self.regionEntry.get() region[dim] = (r, r1) self.setSpectrumConditions() self.updateConditionTable() def getRegionMax(self, spectrumCondition, dim): #print 'getRegionMax' (condition, region) = spectrumCondition self.regionEntry.set(region[dim][1]) def setRegionMax(self, spectrumCondition, dim): #print 'setRegionMax' (condition, region) = spectrumCondition (r0, r1) = region[dim] r = self.regionEntry.get() region[dim] = (r0, r) self.setSpectrumConditions() self.updateConditionTable() def setRegion(self, *extra): spectrumCondition = self.getCurrentObject() if spectrumCondition and self.col is not None: col = self.col - 1 # -1 because of condition dim = col / 2 if col % 2: # max self.setRegionMax(spectrumCondition, dim) else: # min self.setRegionMin(spectrumCondition, dim) """ not needed for now def getCondition(self, spectrumCondition): #print 'getCondition' (condition, region) = spectrumCondition self.conditionMenu.set(condition) def setCondition(self, spectrumCondition): #print 'setCondition' spectrumCondition[0] = self.conditionMenu.get() self.setSpectrumConditions() def selectedCondition(self, ind, condition): spectrumCondition = self.getCurrentObject() if spectrumCondition is not None: self.setCondition(spectrumCondition) """ def getSpectrumConditions(self): spectrum = self.spectrum if not spectrum: return [] application = self.project.application spectrumConditions = application.getValue(spectrum, keyword='contourFileConditions') if spectrumConditions: spectrumConditions = eval(spectrumConditions) else: region = [] for i in range(spectrum.numDim): dim = i + 1 region.append(self.getWholeRegion(spectrum, dim)) spectrumCondition = ['include', region] spectrumConditions = [spectrumCondition] self.setSpectrumConditions(spectrumConditions) return spectrumConditions def setSpectrumConditions(self, spectrumConditions = None): spectrum = self.spectrum if spectrum: if not spectrumConditions: spectrumConditions = self.spectrumConditions application = self.project.application application.setValue(spectrum, keyword='contourFileConditions', value=str(spectrumConditions)) def getDimMin(self, spectrum, dim): dataDim = spectrum.findFirstDataDim(dim=dim) if dataDim.className == 'SampledDataDim': r = 1.0 else: converter = UnitConverter.pnt2ppm dataDimRef = ExperimentBasic.getPrimaryDataDimRef(dataDim) r = converter(float(dataDim.numPoints), dataDimRef) return r def getDimMax(self, spectrum, dim): dataDim = spectrum.findFirstDataDim(dim=dim) if dataDim.className == 'SampledDataDim': r = float(dataDim.numPoints) else: converter = UnitConverter.pnt2ppm dataDimRef = ExperimentBasic.getPrimaryDataDimRef(dataDim) r = converter(1.0, dataDimRef) return r def getWholeRegion(self, spectrum, dim): rMin = self.getDimMin(spectrum, dim) rMax = self.getDimMax(spectrum, dim) return (rMin, rMax) def getCurrentObject(self): # sometimes row highlighting stops so obj passed into selectCell might no longer be current # so instead use direct interrogation of scrolledMatrix obj = self.conditionTable.currentObject return obj def selectCell(self, obj, row, col): # see note about obj in getCurrentObject() self.col = col def addCondition(self): spectrum = self.spectrum if spectrum: ndim = spectrum.numDim spectrumCondition = ['exclude', ndim*[(None,None)]] self.spectrumConditions.append(spectrumCondition) self.setSpectrumConditions() self.updateConditionTable() def deleteCondition(self): spectrumCondition = self.getCurrentObject() if spectrumCondition: self.spectrumConditions.remove(spectrumCondition) self.setSpectrumConditions() self.updateConditionTable() def continueIfFileNameUsed(self, fileName): result = True storedContoursToDelete = [] for analysisSpectrum in self.analysisProject.analysisSpectra: for storedContour in analysisSpectrum.storedContours: if storedContour.fullPath == fileName: storedContoursToDelete.append(storedContour) if storedContoursToDelete: if len(storedContoursToDelete) == 1: s = '' t = 's' else: s = 's' t = '' result = showYesNo('File used', 'Stored contour%s already use%s this fileName, and will be deleted: continue?' % (s, t), parent=self) if result: for storedContour in storedContoursToDelete: try: storedContour.delete() except: pass return result def contourAndSaveSpectrum(self): spectrum = self.spectrum if not spectrum: return if not self.spectrumConditions: return ###self.saveFile() fileName = self.file_entry.get() if not fileName: showError('No filename', 'No filename given', parent=self) return contourDir = self.getContourDir() fullPath = joinPath(contourDir, fileName) if not self.continueIfFileNameUsed(fullPath): return directory = os.path.dirname(fullPath) if not os.path.exists(directory): os.makedirs(directory) dims = self.dim_menu.getText() (xdim, ydim) = [int(dim) for dim in dims.split(', ')] ##self.saveLevels() ##levels = self.levels_entry.get() ##if not levels: ## showError('No levels', 'No contour levels given', parent=self) ## return analysisSpectrum = spectrum.analysisSpectrum levels = analysisSpectrum.posLevels + analysisSpectrum.negLevels scale = spectrum.scale / self.analysisProject.globalContourScale levels = [ level/scale for level in levels ] spectrumCondition = self.spectrumConditions[0] (condition, region) = spectrumCondition ndim = spectrum.numDim firstInt = ndim * [0] lastInt = ndim * [0] for i in range(ndim): try: (firstInt[i], lastInt[i]) = self.convertToPoints(spectrum, i, region[i]) except Exception, e: showError('Invalid region', str(e), parent=self) try: #print 'about to saveSpectrumContours', fullPath, xdim, ydim, levels, firstInt, lastInt saveSpectrumContours(spectrum, fullPath, xdim, ydim, levels, firstInt, lastInt, mem_cache=self.parent.mem_cache) except Exception, e: showError('Save error', str(e), parent=self) return
class EditMeasurementListsPopup(BasePopup): """ **The NMR Derived Measurements and Their Lists** The purpose of this popup window is to display lists of measurements within the current CCPN project. A "Measurement" in this regard is a value that is derived from NMR data and connects to one or more resonances (which may be assigned to atoms). The most commonly encountered kind of measurement is the chemical shift, which is usually derived from recording the positions of peaks in spectra. In this instance the chemical shift measurement of a resonance is made when it is assigned to the dimension of a peak. Because CCPN allows multiple shift lists (technically a type of measurement list) a resonance may have a several chemical shift measurements; useful studying different conditions. There are several different kinds of measurement and hence measurement list that may be included within a CCPN project, for example J-coupling, hydrogen exchange rate, chemical shift anisotropy, T1 relaxation and chemical shift. All types will be displayed by this popup if they are available within the current project, although Analysis does not necessarily have tools to record all of these different kinds of measurement. The layout of this popup consists of two tabs, one for the organising lists that contain the measurements and another to display the individual measurements within a single list. With the exception of blank lists for chemical shifts, measurement lists are not made using this popup, instead they are made at the point where an analysis is performed, e.g. T1 lists are made with data from the `Follow Intensity Changes`_ tool. The second tab, containing the "Measurements" table displays the details of the individual measured values within a selected list. What the values mean and which units they are in, if any, naturally depends on the kind of list being viewed. If measurements are made within CCPN software (as opposed to being imported) then the individual measurements usually record the spectrum peaks that were used in the calculation of the measured value, e.g. the peaks for which positions record chemical shift or intensities record T1. **Caveats & Tips** If there is no chemical shift list within a project a new one will be made automatically to record the shifts of any assignments. The shift list with which an experiment is associate may be changed via the "Shift List" column of the main `Experiments`_ table. Measurements other than chemical shift, like T1 and T2 relaxation times will only appear within this system if a measurement list entity is formally made. Simply measuring values from spectra, for example using the `Follow Intensity Changes`_ tool .. _`Follow Intensity Changes`: CalcRatesPopup.html .. _`Experiments`: EditExperimentPopup.html """ def __init__(self, parent, *args, **kw): self.guiParent = parent self.experiment = None self.measurementList = None self.measurementListB = None self.measurement = None self.waitingList = False self.waitingMeasurement = False BasePopup.__init__(self, parent, title="Resonance : Measurement Lists", **kw) def body(self, guiFrame): self.geometry('600x600') guiFrame.expandGrid(0, 0) tipTexts = [ 'A table of all the NMR measurement lists in the project, including shift lists, T1 lists, J-coupling lists etc.', 'A table listing all of the individual measurements within an NMR measurement list' ] options = ['Measurement Lists', 'Measurements Table'] tabbedFrame = TabbedFrame(guiFrame, options=options, grid=(0, 0), tipTexts=tipTexts) self.tabbedFrame = tabbedFrame frameA, frameB = tabbedFrame.frames # Measurement Lists frameA.expandGrid(1, 0) self.detailsEntry = Entry(self, text='', returnCallback=self.setDetails, width=12) self.nameEntry = Entry(self, text='', returnCallback=self.setName, width=12) row = 0 frame0 = Frame(frameA, grid=(row, 0), gridSpan=(1, 2)) frame0.grid_columnconfigure(2, weight=1) label = Label(frame0, text='Experiment:', grid=(0, 0), sticky='e') tipText = 'Selects an experiment, if required, to restrict the measurement list table display; showing only lists which were derived using the experiment' self.experimentPulldown = PulldownList(frame0, callback=self.setExperiment, grid=(0, 1), tipText=tipText) row += 1 tipTexts = [ 'The serial number of measurement list', 'The type of measurement list, e.g. shift list, T1 list, J coupling list', 'A short identifying name for the list, for graphical displays', 'The number of measurements contained within the list', 'The unit of measurement used for the values in the list', 'The names of the experiments which were used to derive the measurements', 'A user-specified textual comment for the measurement list' ] justifyList = [ 'center', 'center', 'center', 'center', 'center', 'left' ] #colHeadings = ['List Type','Name','Size','Unit','Experiments','Other Info','Details'] colHeadings = [ '#', 'List Type', 'Name', 'Size', 'Unit', 'Experiments', 'Details' ] editWidgets = [ None, None, self.nameEntry, None, None, None, None, self.detailsEntry ] editGetCallbacks = [ None, None, self.getName, None, None, None, None, self.getDetails ] editSetCallbacks = [ None, None, self.setName, None, None, None, None, self.setDetails ] self.listsMatrix = ScrolledMatrix( frameA, grid=(row, 0), gridSpan=(1, 2), justifyList=justifyList, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, headingList=colHeadings, callback=self.selectListCell, deleteFunc=self.deleteMeasurementList, tipTexts=tipTexts) row += 1 tipTexts = [ 'Show a table of the individual measurements within the selected measurement list', 'Make a new, blank chemical shift list within the project', 'Make a synthetic chemical shift list using random coil values, adjusting protein backbone values for sequence where approprate', 'Delete the selected measurement list' ] texts = [ 'Show Measurements', 'New Shift List', 'Make Random Coil Shift List', 'Delete' ] commands = [ self.showMeasurements, self.addShiftList, self.makeRandomCoilShiftList, self.deleteMeasurementList ] self.listButtons = ButtonList(frameA, texts=texts, commands=commands, grid=(row, 0), gridSpan=(1, 2), tipTexts=tipTexts) # Measurements self.measurementDetailsEntry = Entry( self, text='', returnCallback=self.setMeasurementDetails, width=12) self.measurementMeritEntry = FloatEntry( self, returnCallback=self.setMeasurementMerit, width=6) row = 0 frame0 = Frame(frameB, grid=(row, 0)) frame0.grid_columnconfigure(2, weight=1) label = Label(frame0, text='Measurement List:', grid=(0, 0), sticky='e') tipText = 'Selects the measurement list to display measurements for' self.listPulldown = PulldownList(frame0, callback=self.setMeasurementList, grid=(0, 1), tipText=tipText) row += 1 frameB.expandGrid(row, 0) tipTexts = [ 'The serial number of the measurement within its containing list', 'The number or assignment of the NMR resonance(s) to which the measurement applies', 'The numeric value of the NMR measurement on the specified resonance(s), and the unit of measurement', 'The standard deviation error in the measured value', 'The molecular chain, if any, to which the measurement relates by virtue of atom assigned resonances', 'The isotope type(s) of the measures resonance(s)', 'A figure-of-merit value for the measurement indicating its quality or reliability', 'The number of peaks in the CCPN project used to take the measurement', 'A user-defined textual comment for the measurement' ] justifyList = [ 'center', 'center', 'center', 'center', 'center', 'center', 'center', 'center', 'center', 'left' ] colHeadings = [ '#', 'Resonance', 'Value', 'SD', 'Chain', 'Isotope', 'Fig of\nMerit', 'Peaks', 'Details' ] editWidgets = [ None, None, None, None, None, None, self.measurementMeritEntry, None, self.measurementDetailsEntry ] editGetCallbacks = [ None, None, None, None, None, None, self.getMeasurementMerit, self.showPeaks, self.getMeasurementDetails ] editSetCallbacks = [ None, None, None, None, None, None, self.setMeasurementMerit, None, self.setMeasurementDetails ] self.measurementsMatrix = ScrolledMatrix( frameB, grid=(row, 0), multiSelect=True, tipTexts=tipTexts, justifyList=justifyList, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, headingList=colHeadings, callback=self.selectMeasurementCell) row += 1 tipTexts = [ 'Show a table containing peaks that were used to derive the selected measurements', 'For some measurement lists (currently only shift lists) manually trigger a recalculation of values', 'Show a table containing the resonances that relate to the selected measurements', 'Delete the selected measurement records; cannot be done for chemical shift values still ties to peaks via assignment' ] texts = ['Show Peaks', 'Recalculate', 'Show Resonances', 'Delete'] commands = [ self.showPeaks, self.recalculateMeasurements, self.showResonances, self.deleteMeasurements ] self.measurementButtons = ButtonList(frameB, texts=texts, grid=(row, 0), commands=commands, tipTexts=tipTexts) # Main Frame self.bottomButtons = UtilityButtonList(tabbedFrame.sideFrame, helpUrl=self.help_url, grid=(0, 0), gridSpan=(1, 2), sticky='e') self.updateMeasurementListAfter() self.updateMeasurementsAfter() self.administerNotifiers(self.registerNotify) def administerNotifiers(self, notifyFunc): for func in ('__init__', 'delete', 'setName', 'setDipolarRelaxList', 'setHExchProtectionList', 'setHExchRateList', 'setNoeList', 'setJCouplingList', 'setRdcList', 'setShiftAnisotropyList', 'setShiftDifferenceList', 'setShiftList', 'setT1List', 'setT1RhoList', 'setT2List'): for clazz in ('ccp.nmr.Nmr.Experiment', ): notifyFunc(self.updateExperiments, clazz, func) for func in ('__init__', 'delete', 'setDetails', 'setName', 'addExperiment', 'removeExperiment', 'setExperiments'): for clazz in ('ccp.nmr.Nmr.DipolarRelaxList', 'ccp.nmr.Nmr.HExchProtectionList', 'ccp.nmr.Nmr.HExchRateList', 'ccp.nmr.Nmr.NoeList', 'ccp.nmr.Nmr.JCouplingList', 'ccp.nmr.Nmr.RdcList', 'ccp.nmr.Nmr.ShiftAnisotropyList', 'ccp.nmr.Nmr.ShiftDifferenceList', 'ccp.nmr.Nmr.ShiftList', 'ccp.nmr.Nmr.T1List', 'ccp.nmr.Nmr.T1RhoList', 'ccp.nmr.Nmr.T2List'): notifyFunc(self.updateMeasurementListAfter, clazz, func) # Measurements for func in ('__init__', 'delete'): for clazz in ('ccp.nmr.Nmr.DipolarRelaxation', 'ccp.nmr.Nmr.HExchProtection', 'ccp.nmr.Nmr.HExchRate', 'ccp.nmr.Nmr.Noe', 'ccp.nmr.Nmr.JCoupling', 'ccp.nmr.Nmr.Rdc', 'ccp.nmr.Nmr.Shift', 'ccp.nmr.Nmr.ShiftAnisotropy', 'ccp.nmr.Nmr.ShiftDifference', 'ccp.nmr.Nmr.T1', 'ccp.nmr.Nmr.T1Rho', 'ccp.nmr.Nmr.T2'): notifyFunc(self.updateMeasurementAfter, clazz, func) for func in ('__init__', 'delete', 'setDetails', 'setError', 'setFigOfMerit', 'setValue', 'setPeaks', 'addPeak', 'removePeak'): for clazz in ('ccp.nmr.Nmr.DipolarRelaxation', 'ccp.nmr.Nmr.HExchProtection', 'ccp.nmr.Nmr.HExchRate', 'ccp.nmr.Nmr.Noe', 'ccp.nmr.Nmr.JCoupling', 'ccp.nmr.Nmr.Rdc', 'ccp.nmr.Nmr.Shift', 'ccp.nmr.Nmr.ShiftAnisotropy', 'ccp.nmr.Nmr.ShiftDifference', 'ccp.nmr.Nmr.T1', 'ccp.nmr.Nmr.T1Rho', 'ccp.nmr.Nmr.T2'): self.registerNotify(self.updateMeasurementAfter, clazz, func) def open(self): self.updateMeasurementListAfter() self.updateMeasurementsAfter() BasePopup.open(self) def makeRandomCoilShiftList(self): molSystems = self.project.molSystems shiftList = makeRandomCoilShiftList(molSystems) def addShiftList(self): shiftList = newShiftList(self.project) self.setExperiment(None) def getName(self, measurementList): if measurementList: self.nameEntry.set(measurementList.name) def setName(self, event): text = self.nameEntry.get() if text and text != ' ': self.measurementList.setName(text) def getDetails(self, measurementList): if measurementList and measurementList.details: self.detailsEntry.set(measurementList.details) def setDetails(self, event): text = self.detailsEntry.get() if text and text != ' ': self.measurementList.setDetails(text) def setExperiment(self, experiment): if experiment is not self.experiment: self.experiment = experiment self.measurementList = None self.updateMeasurementListAfter() def deleteMeasurementList(self, *event): if self.measurementList: if (len(self.measurementList.experiments) > 0) and (self.measurementList.className == 'ShiftList'): showWarning( 'Warning', 'Deletion of shift lists with associated experiments prohibited', parent=self) return elif len(self.measurementList.measurements) > 0: if showOkCancel('Confirm', 'List is not empty. Really delete?', parent=self): self.measurementList.delete() self.measurementList = None else: return else: self.measurementList.delete() self.measurementList = None def showMeasurements(self): self.updateMeasurements(self.measurementList) self.tabbedFrame.select(1) def selectListCell(self, object, row, col): self.measurementList = object if self.measurementList: self.listButtons.buttons[0].enable() self.listButtons.buttons[3].enable() self.setMeasurementList(self.measurementList) def updateMeasurementAfter(self, measurement): if (self.experiment is None) or ( self.experiment in measurement.parentList.experiments): self.updateMeasurementListAfter() if measurement.parentList is self.measurementListB: self.updateMeasurementsAfter() def updateMeasurementListAfter(self, measurementList=None): self.updateListPulldown() if self.waitingList: return if (measurementList is None) \ or (self.experiment is None) \ or (self.experiment in measurementList.experiments): self.waitingList = True self.after_idle(self.updateListsTable) def updateExperiments(self, null=None): index = 0 experiments = [ None, ] + self.nmrProject.sortedExperiments() names = [ '<Any>', ] + [e.name for e in experiments[1:]] experiment = self.experiment if experiments: if experiment not in experiments: experiment = experiments[0] index = experiments.index(experiment) else: experiment = None if self.experiment is not experiment: self.experiment = experiment self.updateMeasurementListAfter() self.experimentPulldown.setup(names, experiments, index) def updateListsTable(self, experiment=None): if experiment is None: experiment = self.experiment else: self.experiment = experiment if self.experiment is None: self.measureList = None self.updateExperiments() if self.measurementList: self.listButtons.buttons[0].enable() self.listButtons.buttons[3].enable() else: self.listButtons.buttons[0].disable() self.listButtons.buttons[3].disable() if self.experiment == None: measurementLists = self.nmrProject.sortedMeasurementLists() else: measurementLists = [] lists = self.nmrProject.sortedMeasurementLists() for measurementList in lists: if self.experiment in measurementList.experiments: measurementLists.append(measurementList) objectList = [] textMatrix = [] for measurementList in measurementLists: objectList.append(measurementList) names = ['%s' % (e.name) for e in measurementList.experiments] if len(names) > 6: names.insert(int(len(names) / 2), '\n') experimentText = ' '.join(names) """otherInfoText = '' for attribute in measurementList.metaclass.getAllAttributes(): name = attribute.name otherInfoText += '%s:%s ' % (name,getattr(measurementList, name)) for role in measurementList.metaclass.getAllRoles(): name = role.name otherInfoText += '%s:%s ' % (name,getattr(measurementList, name))""" datum = [ measurementList.serial, measurementList.className, measurementList.name, len(measurementList.measurements), measurementList.unit, experimentText, #otherInfoText, measurementList.details ] textMatrix.append(datum) if not objectList: textMatrix.append([]) self.listsMatrix.update(objectList=objectList, textMatrix=textMatrix) self.waitingList = False def destroy(self): self.administerNotifiers(self.unregisterNotify) BasePopup.destroy(self) # Measurement functions def deleteMeasurements(self): measurements = self.measurementsMatrix.currentObjects if measurements: className = self.measurementListB.className if className == 'ShiftList': msg = 'Really delete %s chemical shifts?' % len(measurements) if not showOkCancel('Confirm', msg, parent=self): return untouched = set() for shift in measurements: for peakDim in shift.peakDims: shiftList = peakDim.peak.peakList.dataSource.experiment.shiftList if shiftList is self.measurementListB: untouched.add(shift) break else: shift.delete() if untouched: if len(untouched) > 1: msg = 'Could not delete %d shifts because they are still assigned to peaks' % len( untouched) else: msg = 'Could not delete shift because it is still assigned to peaks' showWarning('Warning', msg, parent=self) else: msg = 'Really delete %d %s measurements?' % (len(measurements), className[:-4]) if not showOkCancel('Confirm', msg, parent=self): return for measurement in measurements: measurement.delete() def recalculateMeasurements(self): measurements = self.measurementsMatrix.currentObjects if measurements and (self.measurementListB.className == 'ShiftList'): for shift in measurements: averageShiftValue(shift) def showPeaks(self, *event): measurements = self.measurementsMatrix.currentObjects if measurements: peaksDict = {} for measurement in measurements: for peak in measurement.peaks: peaksDict[peak] = 1 peaks = peaksDict.keys() if len(peaks) > 0: self.guiParent.viewPeaks(peaks) def showResonances(self, *event): measurements = self.measurementsMatrix.currentObjects if measurements: resonances = set() for measurement in measurements: if hasattr(measurement, 'resonances'): resonances.update(measurement.resonances) else: resonances.add(measurement.resonance) if resonances: self.guiParent.viewSelectedResonances(resonances) def setMeasurementList(self, measurementList): if measurementList is not self.measurementListB: self.measurementListB = measurementList self.updateMeasurementsAfter() def getMeasurementMerit(self, measurement): if measurement: self.measurementMeritEntry.set(measurement.figOfMerit) def setMeasurementMerit(self, event): value = self.measurementMeritEntry.get() if value is not None: self.measurement.setFigOfMerit(max(0.0, min(1.0, float(value)))) def getMeasurementDetails(self, measurement): if measurement and measurement.details: self.measurementDetailsEntry.set(measurement.details) def setMeasurementDetails(self, event): text = self.measurementDetailsEntry.get() if text and text != ' ': self.measurement.setDetails(text) def selectMeasurementCell(self, object, row, col): self.measurement = object if self.measurement: self.measurementButtons.buttons[0].enable() self.measurementButtons.buttons[2].enable() if self.measurementListB.className == 'ShiftList': self.measurementButtons.buttons[1].enable() else: self.measurementButtons.buttons[1].disable() def getMeasurementListName(self, measurementList): name = measurementList.name or \ '%d:%s' % (measurementList.serial,measurementList.className) return name def updateListPulldown(self, null=None): index = 0 measurementLists = self.nmrProject.sortedMeasurementLists() nameFunc = self.getMeasurementListName names = [nameFunc(ml) for ml in measurementLists] measurementList = self.measurementListB if measurementLists: if measurementList not in measurementLists: measurementList = measurementLists[0] index = measurementLists.index(measurementList) else: measurementList = None if self.measurementListB is not measurementList: self.measurementListB = measurementList self.updateMeasurementsAfter() self.listPulldown.setup(names, measurementLists, index) def updateMeasurementsAfter(self, *opt): if self.waitingMeasurement: return else: self.waitingMeasurement = True self.after_idle(self.updateMeasurements) def updateMeasurements(self, measurementList=None): headingList = [ '#', 'Resonance', 'Value', 'SD', 'Chain', 'Isotope', 'Fig of\nMerit', 'Peaks', 'Details' ] if measurementList is not None: self.measurementListB = measurementList self.updateListPulldown() if self.measurementListB is None: self.measurement = None measurements = [] else: if self.measurementListB.unit: headingList[2] = 'Value\n(%s)' % (self.measurementListB.unit) measurements = self.measurementListB.sortedMeasurements() if measurements and hasattr(measurements[0], 'resonances'): headingList[1] = 'Resonances' headingList[5] = 'Isotopes' if self.measurement: self.measurementButtons.buttons[0].enable() self.measurementButtons.buttons[2].enable() if self.measurementListB.className == 'ShiftList': self.measurementButtons.buttons[1].enable() else: self.measurementButtons.buttons[1].disable() else: self.measurementButtons.buttons[0].disable() self.measurementButtons.buttons[1].disable() self.measurementButtons.buttons[2].disable() objectList = [] textMatrix = [] i = 0 for measurement in measurements: i += 1 resonanceText = '' isotopeText = '' chainText = '' if hasattr(measurement, 'resonances'): resonanceText = ' '.join( [makeResonanceGuiName(r) for r in measurement.resonances]) isotopeText = ' '.join( [r.isotopeCode for r in measurement.resonances]) residueDict = {} for resonance in measurement.resonances: if resonance.resonanceSet: residue = resonance.resonanceSet.findFirstAtomSet( ).findFirstAtom().residue residueDict[residue] = None keys = residueDict.keys() if len(keys) == 1: residue = keys[0] residueNum = residue.seqCode chainText = '%s:%s' % (residue.chain.molSystem.code, residue.chain.code) elif hasattr(measurement, 'resonance'): resonance = measurement.resonance resonanceText = makeResonanceGuiName(resonance) isotopeText = measurement.resonance.isotopeCode if resonance.resonanceSet: residue = resonance.resonanceSet.findFirstAtomSet( ).findFirstAtom().residue chainText = '%s:%s' % (residue.chain.molSystem.code, residue.chain.code) datum = [ i, resonanceText, measurement.value, measurement.error, chainText, isotopeText, measurement.figOfMerit, len(measurement.peaks), measurement.details or ' ' ] objectList.append(measurement) textMatrix.append(datum) if not objectList: textMatrix.append([]) tipTexts = self.measurementsMatrix.tipTexts # unchanged, despite variable headings self.measurementsMatrix.update(headingList=headingList, objectList=objectList, textMatrix=textMatrix, tipTexts=tipTexts) self.waitingMeasurement = False
class OpenMacroPopup(BasePopup): """ **Locate Python Macro Scripts On Disk** This popup is used to locate a Python file and function definition on disk so that it can be loaded into Analysis as a "Macro". The top table is a file browser that the user can navigate to the location of the Python file (typically sending in ".py"). When a Python file is selected in the upper table the system will look through the contents of the file to find any macro functions that are defined within. Note that a macro function is defined by the normal Python "def" keyword but must take "argSever" as its first input argument. Any macro functions in the selected file will be displayed in the lower table. Clicking on the name of a function in this table selects it for opening. The user may edit the name of the macro for display purposes within Analysis. Finally clicking [Load Macro] will add it to the CCPN project, stored in a user-specific way inside the current Analysis profile. Once loaded into the project the new macro will appear in the main macro table (the "Macros" tab of `User Options`_) .. _`User Options`: EditProfilesPopup.html """ def __init__(self, parent, **kw): # TBD: properly transient = True BasePopup.__init__(self, parent=parent, title='Open macro', transient=transient, **kw) def body(self, guiParent): guiParent.grid_columnconfigure(1,weight=1) row = 0 file_types = [ FileType('Python', ['*.py']), FileType('All', ['*']) ] self.file_select = FileSelect(guiParent, file_types=file_types, single_callback=self.chooseFile, double_callback=self.chooseFile) self.file_select.grid(row=row, column=0, columnspan=2, sticky='nsew') row = row + 1 headingList=('Function',) self.scrolledMatrix = ScrolledMatrix(guiParent, initialRows=4, headingList=headingList, callback=self.selectFunction) self.scrolledMatrix.grid(row=row, column=0, columnspan=2, sticky='nsew') guiParent.grid_rowconfigure(row,weight=1) row = row + 1 self.moduleLabel1 = Label(guiParent, text='Module: ') self.moduleLabel1.grid(row=row, column=0, sticky='nw') self.moduleLabel2 = Label(guiParent, text=' ') self.moduleLabel2.grid(row=row, column=1, sticky='nw') row = row + 1 self.functionLabel1 = Label(guiParent, text='Function: ') self.functionLabel1.grid(row=row, column=0, sticky='nw') self.functionLabel2 = Label(guiParent, text=' ') self.functionLabel2.grid(row=row, column=1, sticky='nw') row = row + 1 self.nameLabel = Label(guiParent, text='Name: ') self.nameLabel.grid(row=row, column=0, sticky='nw') self.nameEntry = Entry(guiParent, text=' ', width=40) self.nameEntry.grid(row=row, column=1, sticky='nw') row = row + 1 texts = [ 'Load Macro' ] commands = [ self.loadMacro ] buttons = UtilityButtonList(guiParent, texts=texts, commands=commands, helpUrl=self.help_url) buttons.grid(row=row, column=0, columnspan=2, sticky='ew') self.loadButton = buttons.buttons[0] self.loadButton.disable() self.path = None self.module = None self.function = None def selectFunction(self, function, row, col): if function: self.loadButton.enable() self.function = function self.functionLabel2.set(function) self.nameEntry.set(function) def updateFunctions(self, file): self.loadButton.disable() functions = [] fileHandle = open(file, 'r') line = fileHandle.readline() textMatrix = [] while line : match = re.match('\s*def\s+(\w+)\s*\(.*argServer',line) if match: function = match.group(1) functions.append(function) textMatrix.append( [function,] ) line = fileHandle.readline() self.scrolledMatrix.update(objectList=functions,textMatrix=textMatrix) def chooseFile(self, file): fullPath =self.file_select.getFile() if not os.path.isdir(fullPath) and file: (self.path, module) = splitPath(fullPath) module = re.sub('\..+?$','',module) self.module = module self.moduleLabel2.set(module) self.updateFunctions(file) def loadMacro(self): if self.module and self.function and self.path: name = self.nameEntry.get() if not name: name = self.function analysisProfile = self.analysisProfile m1 = analysisProfile.newMacro(name=name,path=self.path, function=self.function, module=self.module,ordering=1) self.close()
class AriaRunFrame(NmrCalcRunFrame): def __init__(self, parent, project, calcStore, *args, **kw): NmrCalcRunFrame.__init__(self, parent, project, calcStore, inputTypes=(PEAK_DATA, CONSTRAINT_DATA), chainSelection=True, *args, **kw) self.calcStore = calcStore self.optPeakList = None self.optConstraintList = None frame = self.tabbedFrame.frames[1] headingList = ['PeakList','Filter\nViolated?', 'Keep\nAssignments?','Shift List'] editWidgets = [None, None, None, None] editGetCallbacks = [None, self.toggleFilterViol, self.toggleKeepAssign, None] editSetCallbacks = [None, None, None, None] row = 0 subFrame = Frame(frame, grid=(row,0)) subFrame.expandGrid(0,1) label = Label(subFrame, text='File Name Prefix:', grid=(0,0)) self.filePrefixEntry = Entry(subFrame, text='aria', grid=(0,1), sticky="ew") self.filePrefixEntry.bind('<Leave>', self.updateEntryParams) label = Label(subFrame, text='CNS Executable:', grid=(1,0)) self.cnsExeEntry = Entry(subFrame, text='', grid=(1,1), sticky="ew") self.cnsExeEntry.bind('<Leave>', self.updateEntryParams) button = Button(subFrame, text='Select File',bd=1,bg='#C0E0FF', command=self.selectCnsExe, grid=(1,2)) label = Label(subFrame, text='Working Directory:', grid=(2,0)) self.workingDirEntry = Entry(subFrame, text='', grid=(2,1), sticky="ew") self.workingDirEntry.bind('<Leave>', self.updateEntryParams) button = Button(subFrame, text='Select File',bd=1,bg='#C0E0FF', command=self.selectWorkingDir, grid=(2,2)) label = Label(subFrame, text='Temporary Directory:', grid=(3,0)) self.tempDirEntry = Entry(subFrame, text='', grid=(3,1), sticky="ew") self.tempDirEntry.bind('<Leave>', self.updateEntryParams) button = Button(subFrame, text='Select File',bd=1,bg='#C0E0FF', command=self.selectTempDir, grid=(3,2)) row += 1 frame.expandGrid(row,0) self.grid_rowconfigure(row, weight=1) self.optPeakListMatrix = ScrolledMatrix(frame, headingList=headingList, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, multiSelect=True, grid=(row,0), callback=self.selectOptPeakList) row += 1 frame.expandGrid(row,0) headingList = ['Constraint List', 'Name', 'Filter\nViolated?', 'Ambiguous\nProtocol?',] editWidgets = [None, None, None, None] editGetCallbacks = [None, None, self.toggleFilterViol, self.toggleAmbig] editSetCallbacks = [None, None, None, None] self.optConstraintMatrix = ScrolledMatrix(frame, headingList=headingList, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, multiSelect=True, grid=(row,0), callback=self.selectConstraintList) self.optConstraintMatrix.doEditMarkExtraRules = self.doEditMarkExtraRules self.update(calcStore) def update(self, calcStore=None): NmrCalcRunFrame.update(self, calcStore) self.updateSettings() # Need to run NmrCalcRunFrame.update before # this to get new self.run, self.project if self.run: repository = self.project.findFirstRepository(name='userData') defaultDir = repository.url.path filePrefix = getRunTextParameter(self.run, FILE_PREFIX) or self.project.name cnsExe = getRunTextParameter(self.run, CNS_EXE) or '/usr/bin/cns' workingDir = getRunTextParameter(self.run, WORKING_DIR) or defaultDir tempDir = getRunTextParameter(self.run, TEMP_DIR) or defaultDir self.filePrefixEntry.set(filePrefix) self.cnsExeEntry.set(cnsExe) self.workingDirEntry.set(workingDir) self.tempDirEntry.set(tempDir) self.updateEntryParams() def updateEntryParams(self, event=None): if self.run: repository = self.project.findFirstRepository(name='userData') defaultDir = repository.url.path filePrefix = self.filePrefixEntry.get() or self.project.name cnsExe = self.cnsExeEntry.get() or '/usr/bin/cns' workingDir = self.workingDirEntry.get() or defaultDir tempDir = self.tempDirEntry.get() or defaultDir setRunParameter(self.run, FILE_PREFIX, filePrefix) setRunParameter(self.run, CNS_EXE, cnsExe) setRunParameter(self.run, WORKING_DIR, workingDir) setRunParameter(self.run, TEMP_DIR, tempDir) def selectCnsExe(self): fileTypes = [ FileType("All", ["*"]), FileType("EXE", ["*.exe"]) ] popup = FileSelectPopup(self, fileTypes) file = popup.getFile() if file: self.cnsExeEntry.set( file ) popup.destroy() self.updateEntryParams() def selectWorkingDir(self): popup = FileSelectPopup(self, show_file=False) directory = popup.getDirectory() if directory: self.workingDirEntry.set( directory ) popup.destroy() self.updateEntryParams() def selectTempDir(self): popup = FileSelectPopup(self, show_file=False) directory = popup.getDirectory() if directory: self.tempDirEntry.set( directory ) popup.destroy() self.updateEntryParams() def getPeakLists(self): # overwrites superclass return getThroughSpacePeakLists(self.project) def administerNotifiers(self, notifyFunc): NmrCalcRunFrame.administerNotifiers(self, notifyFunc) for func in ('__init__','delete','setName'): #notifyFunc(self.updateSettings, 'ccp.nmr.NmrConstraint.AbstractConstraintList', func) notifyFunc(self.updateSettings, 'ccp.nmr.NmrConstraint.DistanceConstraintList', func) notifyFunc(self.updateSettings, 'ccp.nmr.NmrConstraint.DihedralConstraintList', func) notifyFunc(self.updateSettings, 'ccp.nmr.NmrConstraint.HBondConstraintList', func) def selectOptPeakList(self, obj, row, col): self.optPeakList = obj def selectConstraintList(self, obj, row, col): self.optConstraintList = obj def doEditMarkExtraRules(self, obj, row, col): if col in (2,3): cSet = obj.nmrConstraintStore if cSet: for cList in obj.constraintLists: if cList.className[:-14] != 'Distance': # i.e. ambig protocols and viol filtering # is only for dist constraints, not dihedral etc return False return True def toggleUseInCalc(self, dataObj): toggleObjBooleanParameter(dataObj, USE_IN_CALC) self.updateSettings() def toggleFilterViol(self, dataObj): toggleObjBooleanParameter(dataObj, FILTER_VIOL) self.updateSettings() def toggleKeepAssign(self, dataObj): toggleObjBooleanParameter(dataObj, KEEP_ASSIGN) self.updateSettings() def toggleAmbig(self, dataObj): toggleObjBooleanParameter(dataObj, AMBIG_PROTOCOL) self.updateSettings() def updateSettings(self, obj=None): textMatrix = [] objectList = [] run = self.run if run: peakListsData = [] constraintData = [] molSystemData = None for dataObj in run.sortedInputs(): className = dataObj.className if className == 'PeakListData': peakList = dataObj.peakList if peakList: peakListsData.append((dataObj, peakList)) elif className == 'MolSystemData': molSystem = dataObj.molSystem if molSystem: molSystemData = (dataObj, molSystem) elif className == 'ConstraintStoreData': nmrConstraintStore = dataObj.nmrConstraintStore if nmrConstraintStore: constraintLists = dataObj.constraintLists or \ nmrConstraintStore.sortedConstraintLists() # Chould be only one for constraintList in constraintLists: if constraintList is None: # Prob happens when serial no longer exists continue constraintData.append((dataObj, constraintList)) for dataObj, peakList in peakListsData: spectrum = peakList.dataSource experiment = spectrum.experiment filterViol = getObjBooleanParameter(dataObj, FILTER_VIOL) keepAssign = getObjBooleanParameter(dataObj, KEEP_ASSIGN) shiftList = peakList.dataSource.experiment.shiftList if shiftList: shiftListName = '%d:%s' % (shiftList.serial,shiftList.name) else: shiftListName = None ident = '%s:%s:%d' % (experiment.name,spectrum.name,peakList.serial) textMatrix.append([ident, filterViol and 'Yes' or 'No', keepAssign and 'Yes' or 'No', shiftListName]) objectList.append(dataObj) self.optPeakListMatrix.update(textMatrix=textMatrix,objectList=objectList) textMatrix = [] objectList = [] for dataObj, constraintList in constraintData: #useInCalc = getObjBooleanParameter(dataObj, USE_IN_CALC) cSet = dataObj.constraintStoreSerial cType = constraintList.className[:-14] if cType == 'Distance': filterViol = getObjBooleanParameter(dataObj, FILTER_VIOL) ambigProtocol = getObjBooleanParameter(dataObj, AMBIG_PROTOCOL) ambigProtocol = ambigProtocol and 'Yes' or 'No' filterViol = filterViol and 'Yes' or 'No' else: ambigProtocol = None filterViol = None ident = '%s - %d:%d' % (cType, cSet, constraintList.serial) textMatrix.append([ident, constraintList.name, filterViol, ambigProtocol]) objectList.append(dataObj) self.optConstraintMatrix.update(textMatrix=textMatrix, objectList=objectList)
class CingGui(BasePopup): def __init__(self, parent, options, *args, **kw): # Fill in below variable once run generates some results self.haveResults = None # store the options self.options = options BasePopup.__init__(self, parent=parent, title='CING Setup', **kw) # self.setGeometry(850, 750, 50, 50) self.project = None # self.tk_strictMotif( True) self.updateGui() # end def __init__ def body(self, guiFrame): row = 0 col =0 # frame = Frame( guiFrame ) # frame.grid(row=row, column=col, sticky='news') self.menuBar = Menu( guiFrame) self.menuBar.grid( row=row, column=col, sticky='ew') #---------------------------------------------------------------------------------- # Project frame #---------------------------------------------------------------------------------- # guiFrame.grid_columnconfigure(row, weight=1) # frame = LabelFrame(guiFrame, text='Project', font=medFont) row = +1 col =0 frame = LabelFrame(guiFrame, text='Project', **labelFrameAttributes ) print '>', frame.keys() frame.grid(row=row, column=col, sticky='nsew' ) frame.grid_columnconfigure(2, weight=1) # frame.grid_rowconfigure(0, weight=1) srow = 0 self.projectOptions = ['old','new from PDB','new from CCPN','new from CYANA'] self.projOptionsSelect = RadioButtons(frame, selected_index=0, entries=self.projectOptions, direction='vertical', select_callback=self.updateGui ) self.projOptionsSelect.grid(row=srow,column=0,rowspan=len(self.projectOptions),columnspan=2, sticky='w') if self.options.name: text = self.options.name else: text='' # end if self.projEntry = Entry(frame, bd=1, text=text, returnCallback=self.updateGui) self.projEntry.grid(row=srow,column=2,columnspan=2,sticky='ew') # self.projEntry.bind('<Key>', self.updateGui) self.projEntry.bind('<Leave>', self.updateGui) projButton = Button(frame, bd=1,command=self.chooseOldProjectFile, text='browse') projButton.grid(row=srow,column=3,sticky='ew') srow += 1 self.pdbEntry = Entry(frame, bd=1, text='') self.pdbEntry.grid(row=srow,column=2,sticky='ew') self.pdbEntry.bind('<Leave>', self.updateGui) pdbButton = Button(frame, bd=1,command=self.choosePdbFile, text='browse') pdbButton.grid(row=srow,column=3,sticky='ew') srow += 1 self.ccpnEntry = Entry(frame, bd=1, text='') self.ccpnEntry.grid(row=srow,column=2,sticky='ew') self.ccpnEntry.bind('<Leave>', self.updateGui) ccpnButton = Button(frame, bd=1,command=self.chooseCcpnFile, text='browse') ccpnButton.grid(row=srow,column=3,sticky='ew') srow += 1 self.cyanaEntry = Entry(frame, bd=1, text='') self.cyanaEntry.grid(row=srow,column=2,sticky='ew') self.cyanaEntry.bind('<Leave>', self.updateGui) cyanaButton = Button(frame, bd=1,command=self.chooseCyanaFile, text='browse') cyanaButton.grid(row=srow,column=3,sticky='ew') #Empty row srow += 1 label = Label(frame, text='') label.grid(row=srow,column=0,sticky='nw') srow += 1 label = Label(frame, text='Project name:') label.grid(row=srow,column=0,sticky='nw') self.nameEntry = Entry(frame, bd=1, text='') self.nameEntry.grid(row=srow,column=2,sticky='w') #Empty row srow += 1 label = Label(frame, text='') label.grid(row=srow,column=0,sticky='nw') srow += 1 self.openProjectButton = Button(frame, command=self.openProject, text='Open Project', **actionButtonAttributes ) self.openProjectButton.grid(row=srow,column=0, columnspan=4, sticky='ew') #---------------------------------------------------------------------------------- # status #---------------------------------------------------------------------------------- # guiFrame.grid_columnconfigure(1, weight=0) srow = 0 frame = LabelFrame(guiFrame, text='Status', **labelFrameAttributes) frame.grid( row=srow, column=1, sticky='wnes') self.projectStatus = Text(frame, height=11, width=70, borderwidth=0, relief='flat') self.projectStatus.grid(row=0, column=0, sticky='wen') #Empty row srow += 1 label = Label(frame, text='') label.grid(row=srow,column=0,sticky='nw') srow += 1 self.closeProjectButton = Button(frame, command=self.closeProject, text='Close Project', **actionButtonAttributes) self.closeProjectButton.grid(row=srow,column=0, columnspan=4, sticky='ew') #---------------------------------------------------------------------------------- # Validate frame #---------------------------------------------------------------------------------- row +=1 col=0 frame = LabelFrame(guiFrame, text='Validate', **labelFrameAttributes) # frame = LabelFrame(guiFrame, text='Validate', font=medFont) frame.grid(row=row, column=col, sticky='nsew') # frame.grid_columnconfigure(2, weight=1) frame.grid_rowconfigure(0, weight=1) srow = 0 # label = Label(frame, text='validation') # label.grid(row=srow,column=0,sticky='nw') # # self.selectDoValidation = CheckButton(frame) # self.selectDoValidation.grid(row=srow, column=1,sticky='nw' ) # self.selectDoValidation.set(True) # # srow += 1 # label = Label(frame, text='') # label.grid(row=srow,column=0,sticky='nw') # # srow += 1 label = Label(frame, text='checks') label.grid(row=srow,column=0,sticky='nw') self.selectCheckAssign = CheckButton(frame) self.selectCheckAssign.grid(row=srow, column=1,sticky='nw' ) self.selectCheckAssign.set(True) label = Label(frame, text='assignments and shifts') label.grid(row=srow,column=2,sticky='nw') # srow += 1 # self.selectCheckQueen = CheckButton(frame) # self.selectCheckQueen.grid(row=srow, column=4,sticky='nw' ) # self.selectCheckQueen.set(False) # label = Label(frame, text='QUEEN') # label.grid(row=srow,column=5,sticky='nw') # # queenButton = Button(frame, bd=1,command=None, text='setup') # queenButton.grid(row=srow,column=6,sticky='ew') srow += 1 self.selectCheckResraint = CheckButton(frame) self.selectCheckResraint.grid(row=srow, column=1,sticky='nw' ) self.selectCheckResraint.set(True) label = Label(frame, text='restraints') label.grid(row=srow,column=2,sticky='nw') srow += 1 self.selectCheckStructure = CheckButton(frame) self.selectCheckStructure.grid(row=srow, column=1,sticky='nw' ) self.selectCheckStructure.set(True) label = Label(frame, text='structural') label.grid(row=srow,column=2,sticky='nw') srow += 1 self.selectMakeHtml = CheckButton(frame) self.selectMakeHtml.grid(row=srow, column=1,sticky='nw' ) self.selectMakeHtml.set(True) label = Label(frame, text='generate HTML') label.grid(row=srow,column=2,sticky='nw') srow += 1 self.selectCheckScript = CheckButton(frame) self.selectCheckScript.grid(row=srow, column=1,sticky='nw' ) self.selectCheckScript.set(False) label = Label(frame, text='user script') label.grid(row=srow,column=0,sticky='nw') self.validScriptEntry = Entry(frame, bd=1, text='') self.validScriptEntry.grid(row=srow,column=2,columnspan=3, sticky='ew') scriptButton = Button(frame, bd=1,command=self.chooseValidScript, text='browse') scriptButton.grid(row=srow,column=5,sticky='ew') srow += 1 label = Label(frame, text='ranges') label.grid(row=srow,column=0,sticky='nw') self.rangesEntry = Entry( frame, text='' ) self.rangesEntry.grid( row=srow, column=2, columnspan=3, sticky='ew') # self.validScriptEntry = Entry(frame, bd=1, text='') # self.validScriptEntry.grid(row=srow,column=3,sticky='ew') # # scriptButton = Button(frame, bd=1,command=self.chooseValidScript, text='browse') # scriptButton.grid(row=srow,column=4,sticky='ew') srow += 1 texts = ['Run Validation','View Results','Setup QUEEN'] commands = [self.runCing, None, None] buttonBar = ButtonList(frame, texts=texts, commands=commands,expands=True) buttonBar.grid(row=srow, column=0, columnspan=6, sticky='ew') for button in buttonBar.buttons: button.config(**actionButtonAttributes) # end for self.runButton = buttonBar.buttons[0] self.viewResultButton = buttonBar.buttons[1] self.queenButton = buttonBar.buttons[2] #---------------------------------------------------------------------------------- # Miscellaneous frame #---------------------------------------------------------------------------------- row +=0 col=1 # frame = LabelFrame(guiFrame, text='Miscellaneous', font=medFont) frame = LabelFrame(guiFrame, text='Miscellaneous', **labelFrameAttributes) frame.grid(row=row, column=col, sticky='news') frame.grid_columnconfigure(2, weight=1) frame.grid_columnconfigure(4, weight=1,minsize=30) frame.grid_rowconfigure(0, weight=1) # Exports srow = 0 label = Label(frame, text='export to') label.grid(row=srow,column=0,sticky='nw') self.selectExportXeasy = CheckButton(frame) self.selectExportXeasy.grid(row=srow, column=1,sticky='nw' ) self.selectExportXeasy.set(True) label = Label(frame, text='Xeasy, Sparky, TALOS, ...') label.grid(row=srow,column=2,sticky='nw') srow += 1 self.selectExportCcpn = CheckButton(frame) self.selectExportCcpn.grid(row=srow, column=1,sticky='nw' ) self.selectExportCcpn.set(True) label = Label(frame, text='CCPN') label.grid(row=srow,column=2,sticky='nw') srow += 1 self.selectExportQueen = CheckButton(frame) self.selectExportQueen.grid(row=srow, column=1,sticky='nw' ) self.selectExportQueen.set(True) label = Label(frame, text='QUEEN') label.grid(row=srow,column=2,sticky='nw') srow += 1 self.selectExportRefine = CheckButton(frame) self.selectExportRefine.grid(row=srow, column=1,sticky='nw' ) self.selectExportRefine.set(True) label = Label(frame, text='refine') label.grid(row=srow,column=2,sticky='nw') srow += 1 label = Label(frame, text='') label.grid(row=srow,column=0,sticky='nw') # User script srow += 1 label = Label(frame, text='user script') label.grid(row=srow,column=0,sticky='nw') self.selectMiscScript = CheckButton(frame) self.selectMiscScript.grid(row=srow, column=1,sticky='nw' ) self.selectMiscScript.set(False) self.miscScriptEntry = Entry(frame, bd=1, text='') self.miscScriptEntry.grid(row=srow,column=3,sticky='ew') script2Button = Button(frame, bd=1,command=self.chooseMiscScript, text='browse') script2Button.grid(row=srow,column=4,sticky='ew') srow += 1 texts = ['Export','Run Script'] commands = [None, None] buttonBar = ButtonList(frame, texts=texts, commands=commands,expands=True) buttonBar.grid(row=srow, column=0, columnspan=5, sticky='ew') for button in buttonBar.buttons: button.config(**actionButtonAttributes) # end for self.exportButton = buttonBar.buttons[0] self.scriptButton = buttonBar.buttons[1] #---------------------------------------------------------------------------------- # Textarea #---------------------------------------------------------------------------------- row +=1 guiFrame.grid_rowconfigure(row, weight=1) self.outputTextBox = ScrolledText(guiFrame) self.outputTextBox.grid(row=row, column=0, columnspan=2, sticky='nsew') self.redirectConsole() #---------------------------------------------------------------------------------- # Buttons #---------------------------------------------------------------------------------- row +=1 col=0 texts = ['Quit', 'Help'] commands = [self.close, None] self.buttonBar = ButtonList(guiFrame, texts=texts, commands=commands,expands=True) self.buttonBar.grid(row=row, column=col, columnspan=2, sticky='ew') # self.openProjectButton = self.buttonBar.buttons[0] # self.closeProjectButton = self.buttonBar.buttons[1] # self.runButton = self.buttonBar.buttons[0] # self.viewResultButton = self.buttonBar.buttons[1] for button in self.buttonBar.buttons: button.config(**actionButtonAttributes) # end for # end def body def getGuiOptions(self): projectName = self.projEntry.get() index = self.projOptionsSelect.getIndex() if index > 0: makeNewProject = True projectImport = None if index > 1: i = index-2 format = ['PDB','CCPN','CYANA'][i] file = [self.pdbEntry, self.ccpnEntry, self.cyanaEntry][i].get() if not file: showWarning('Failure','No %s file selected' % format) return # end if projectImport = (format, file) # end if else: # Chould also check that any old project file exists makeNewProject = False projectImport = None # end if doValidation = self.selectDoValidation.get() checks = [] if doValidation: if self.selectCheckAssign.get(): checks.append('assignments') # end if if self.selectCheckResraint.get(): checks.append('restraints') # end if if self.selectCheckStructure.get(): checks.append('structural') # end if if self.selectMakeHtml.get(): checks.append('HTML') # end if if self.selectCheckScript.get(): script = self.validScriptEntry.get() if script: checks.append( ('script',script) ) # end if # end if if self.selectCheckQueen.get(): checks.append('queen') # end if # end if exports = [] if self.selectExportXeasy.get(): exports.append('Xeasy') # end if if self.selectExportCcpn.get(): exports.append('CCPN') # end if if self.selectExportQueen.get(): exports.append('QUEEN') # end if if self.selectExportRefine.get(): exports.append('refine') # end if miscScript = None if self.selectMiscScript.get(): script = self.miscScriptEntry.get() if script: miscScript = script # end if # end if return projectName, makeNewProject, projectImport, doValidation, checks, exports, miscScript # end def getGuiOptions def runCing(self): options = self.getGuiOptions() if options: projectName, makeNewProject, projectImport, doValidation, checks, exports, miscScript = options print 'Project name:', projectName print 'Make new project?', makeNewProject print 'Import source:', projectImport print 'Do vailidation?', doValidation print 'Validation checks:', ','.join(checks) print 'Export to:', ','.join(exports) print 'User script:', miscScript # end if # end def runCing # else there was already an error message def chooseOldProjectFile(self): fileTypes = [ FileType('CING', ['project.xml']), FileType('All', ['*']) ] popup = FileSelectPopup(self, file_types = fileTypes, title = 'Select CING project file', dismiss_text = 'Cancel', selected_file_must_exist = True) fileName = popup.getFile() # dirName = popup.getDirectory() if len(fileName) > 0: # Put text into entry,name widgets dummy,name = cing.Project.rootPath(fileName) self.projEntry.configure(state='normal') self.projEntry.set(fileName) self.nameEntry.configure(state='normal') self.nameEntry.set(name) self.nameEntry.configure(state='disabled') # choose the correct radiobutton self.projOptionsSelect.setIndex(0) self.updateGui() # end if #nd if popup.destroy() # end def chooseOldProjectFile def choosePdbFile(self): fileTypes = [ FileType('PDB', ['*.pdb']), FileType('All', ['*'])] popup = FileSelectPopup(self, file_types = fileTypes, title = 'PDB file', dismiss_text = 'Cancel', selected_file_must_exist = True) fileName = popup.getFile() if len(fileName)>0: # Put text into entry widget self.pdbEntry.configure(state='normal') self.pdbEntry.set(fileName) # Put text into name widget _dir,name,dummy = nTpath( fileName ) self.nameEntry.configure(state='normal') self.nameEntry.set(name) # choose the correct radiobutton self.projOptionsSelect.setIndex(1) self.updateGui() #end if popup.destroy() # end def choosePdbFile def chooseCcpnFile(self): fileTypes = [ FileType('XML', ['*.xml']), FileType('All', ['*'])] popup = FileSelectPopup(self, file_types = fileTypes, title = 'CCPN project XML file', dismiss_text = 'Cancel', selected_file_must_exist = True) fileName = popup.getFile() if len(fileName)>0: self.pdbEntry.configure(state='normal') self.pdbEntry.set(fileName) self.projOptionsSelect.setIndex(1) _dir,name,dummy = nTpath( fileName ) self.nameEntry.set(name) #end if self.ccpnEntry.set(fileName) self.projOptionsSelect.setIndex(2) popup.destroy() # end def chooseCcpnFile def chooseCyanaFile(self): # Prepend default Cyana file extension below fileTypes = [ FileType('All', ['*']), ] popup = FileSelectPopup(self, file_types = fileTypes, title = 'CYANA fproject file', dismiss_text = 'Cancel', selected_file_must_exist = True) fileName = popup.getFile() self.cyanaEntry.set(fileName) self.projOptionsSelect.setIndex(3) popup.destroy() # end def chooseCyanaFile def chooseValidScript(self): # Prepend default Cyana file extension below fileTypes = [ FileType('All', ['*']), ] popup = FileSelectPopup(self, file_types = fileTypes, title = 'Script file', dismiss_text = 'Cancel', selected_file_must_exist = True) fileName = popup.getFile() self.validScriptEntry.set(fileName) popup.destroy() # end def chooseValidScript def chooseMiscScript(self): # Prepend default Cyana file extension below fileTypes = [ FileType('All', ['*']), ] popup = FileSelectPopup(self, file_types = fileTypes, title = 'Script file', dismiss_text = 'Cancel', selected_file_must_exist = True) fileName = popup.getFile() self.miscScriptEntry.set(fileName) popup.destroy() # end def chooseMiscScript def openProject(self ): projOption = self.projOptionsSelect.get() if projOption == self.projectOptions[0]: self.openOldProject() elif projOption == self.projectOptions[1]: self.initPdb() # end if if self.project: self.project.gui = self # end if self.updateGui() #end def def openOldProject(self ): fName = self.projEntry.get() if not os.path.exists( fName ): nTerror('Error: file "%s" does not exist\n', fName) #end if if self.project: self.closeProject() # end if self.project = cing.Project.open( name=fName, status='old', verbose=False ) #end def def initPdb(self ): fName = self.pdbEntry.get() if not os.path.exists( fName ): nTerror('Error: file "%s" does not exist\n', fName) #end if self.project = cing.Project.open( self.nameEntry.get(), status='new' ) self.project.initPDB( pdbFile=fName, convention = 'PDB' ) #end def def closeProject(self): if self.project: self.project.close() # end if self.project = None self.updateGui() #end def def updateGui(self, event=None): projOption = self.projOptionsSelect.get() buttons = self.buttonBar.buttons # Disable entries for e in [self.projEntry, self.pdbEntry, self.ccpnEntry, self.cyanaEntry, self.nameEntry]: e.configure(state='disabled') #end for if projOption == self.projectOptions[0]: # Enable entries self.projEntry.configure(state='normal') if (len(self.projEntry.get()) > 0): self.openProjectButton.enable() self.runButton.enable() else: self.openProjectButton.disable() self.runButton.disable() #end if elif projOption == self.projectOptions[1]: # Enable entries self.pdbEntry.configure(state='normal') self.nameEntry.configure(state='normal') if (len(self.pdbEntry.get()) > 0 and len(self.nameEntry.get()) > 0): buttons[0].enable() buttons[1].enable() else: buttons[0].disable() buttons[1].disable() #end if #end if elif projOption == self.projectOptions[2]: # Enable entries self.ccpnEntry.configure(state='normal') self.nameEntry.configure(state='normal') if (len(self.ccpnEntry.get()) > 0 and len(self.nameEntry.get()) > 0): buttons[0].enable() buttons[1].enable() else: buttons[0].disable() buttons[1].disable() #end if #end if elif projOption == self.projectOptions[3]: # Enable entries self.cyanaEntry.configure(state='normal') self.nameEntry.configure(state='normal') if (len(self.cyanaEntry.get()) > 0 and len(self.nameEntry.get()) > 0): buttons[0].enable() buttons[1].enable() else: buttons[0].disable() buttons[1].disable() #end if #end if self.projectStatus.clear() if not self.project: self.projectStatus.setText('No open project') self.closeProjectButton.setText('Close Project') self.closeProjectButton.disable() else: self.projectStatus.setText(self.project.format()) self.closeProjectButton.enable() self.closeProjectButton.setText(sprintf('Close Project "%s"', self.project.name)) #end if #end def def redirectConsole(self): #pipe = TextPipe(self.inputTextBox.text_area) #sys.stdin = pipe pipe = TextPipe(self.outputTextBox.text_area) sys.stdout = pipe nTmessage.stream = pipe # end def redirectConsole # sys.stderr = pipe def resetConsole(self): #sys.stdin = stdin nTmessage.stream = stdout sys.stdout = stdout sys.stderr = stderr # end def resetConsole def open(self): self.redirectConsole() BasePopup.open(self) # end def open def close(self): geometry = self.getGeometry() self.resetConsole() BasePopup.close(self) print 'close:',geometry sys.exit(0) # remove later # end def close def destroy(self): geometry = self.getGeometry() self.resetConsole() BasePopup.destroy(self) print 'destroy:',geometry sys.exit(0) # remove later
class BackupProjectPopup(BasePopup): """ **Create Automatic Project Backup** The purpose of this dialog is to allow the user to create backups of their project automatically. The XML files for the backup go into a separate directory from the project itself. If the project directory is called PROJECTDIR then the default for the backup directory is PROJECTDIR_backup, although that can be changed to something else in this dialog. Other than the backup directory, the user can specify the frequency of the backup, in minutes. The "Apply Auto Settings" does not have to be applied if the user has changed the on/off setting or if the user has changed the Auto-backup frequency and entered a carriage return. The "Do Immediate Backup" is in case the user wants to do a backup then and there. There is no backup for the backup. """ on_off_entries = ['on', 'off'] def __init__(self, parent, help_msg='', help_url='', *args, **kw): self.help_msg = help_msg self.help_url = help_url BasePopup.__init__(self, parent=parent, title='Project : Backup', **kw) def body(self, guiFrame): self.geometry('500x130') guiFrame.grid_columnconfigure(2, weight=1) guiFrame.grid_rowconfigure(3, weight=1) self.alarm_id = None row = 0 tipText = 'Browse for the directory into which project backups will be made' button = Button(guiFrame, text='Directory:', command=self.selectDir, grid=(row, 0), tipText=tipText, sticky='ew') repository = self.project.findFirstRepository(name='backup') if repository: text = repository.url.path else: # this is trouble, but should not happen text = '' tipText = 'Enter the name of the directory into which project backups will be made' self.dir_entry = Entry(guiFrame, text=text, tipText=tipText, width=40, returnCallback=self.applyAuto, grid=(row, 1), gridSpan=(1, 2), sticky='ew') row += 1 label = Label(guiFrame, text='Auto-backup:', grid=(row, 0)) if self.analysisProject.doAutoBackup: ind = 0 else: ind = 1 tipTexts = [ 'Toggle the timed automatic backup on', 'Toggle the timed automatic backup off' ] self.on_off_buttons = RadioButtons(guiFrame, entries=self.on_off_entries, tipTexts=tipTexts, select_callback=self.applyAuto, selected_index=ind, grid=(row, 1)) row += 1 tipText = 'The number of minutes to wait before automatic project backups' label = Label(guiFrame, text='Auto-backup frequency:', grid=(row, 0)) self.freq_entry = IntEntry(guiFrame, text=self.analysisProject.autoBackupFreq, returnCallback=self.applyAuto, tipText=tipText) self.freq_entry.grid(row=row, column=1) label = Label(guiFrame, text=' (minutes)', grid=(row, 2)) row = row + 1 # Blank for expansion row = row + 1 texts = ['Apply Auto Settings', 'Do Immediate Backup'] commands = [self.applyAuto, self.applyManual] tipTexts = [ 'Commit the specified settings and commence automated CCPN project backup', 'Backup the CCPN project now, into the specified backup directory' ] buttons = UtilityButtonList(guiFrame, texts=texts, commands=commands, doClone=False, helpMsg=self.help_msg, helpUrl=self.help_url, tipTexts=tipTexts, grid=(row, 0), gridSpan=(1, 3), sticky='nsew') def selectDir(self): popup = FileSelectPopup(self, show_file=False) dir = popup.getDirectory() popup.destroy() if (dir): self.dir_entry.set(dir) self.setDir() def setDir(self): directory = self.dir_entry.get() if not directory: showWarning('No directory', 'No directory specified, not setting backup directory', parent=self) return if not os.path.abspath(directory): directory = joinPath(os.getcwd(), directory) if os.path.exists(directory): if not os.path.isdir(directory): showWarning('Path not directory', 'Path "%s" exists but is not a directory' % directory, parent=self) return else: if showYesNo( 'Directory does not exist', 'Directory "%s" does not exist, should it be created?' % directory, parent=self): os.mkdir(directory) else: showWarning( 'Directory not created', 'Directory "%s" not created so not setting backup directory' % directory, parent=self) return repository = self.project.findFirstRepository(name='backup') if not repository: showWarning( 'No backup repository', 'No backup repository found (something wrong somewhere)', parent=self) return url = repository.url if url.path != directory: repository.url = Url(path=normalisePath(directory)) def setFreq(self): freq = self.freq_entry.get() if (freq is None): freq = 0 self.analysisProject.autoBackupFreq = freq def setInfo(self): self.setFreq() self.setDir() on_off = (self.on_off_buttons.get() == 'on') self.analysisProject.doAutoBackup = on_off def applyAuto(self, *event): self.setInfo() on_off = self.on_off_buttons.get() if on_off == 'on': self.parent.setBackupOn() else: self.parent.setBackupOff() def applyManual(self): self.setDir() self.parent.doBackup()
class HelpFrame(Frame): def __init__(self, parent, width=70, height=20, xscroll=False, yscroll=True, *args, **kw): apply(Frame.__init__, (self, parent) + args, kw) self.grid_columnconfigure(1, weight=1) row = 0 texts = ('back', 'forward', 'load') commands = (self.prevUrl, self.nextUrl, self.loadUrl) self.buttons = ButtonList(self, texts=texts, commands=commands) self.buttons.grid(row=row, column=0) self.url_entry = Entry(self, returnCallback=self.loadUrl) self.url_entry.grid(row=row, column=1, columnspan=2, sticky=Tkinter.EW) row = row + 1 frame = Frame(self) frame.grid(row=row, column=0, columnspan=3, sticky=Tkinter.W) self.createFindFrame(frame) row = row + 1 self.grid_rowconfigure(row, weight=1) self.help_text = ScrolledHtml(self, width=width, height=height, xscroll=xscroll, yscroll=yscroll, startUrlCallback=self.startOpenUrl, endUrlCallback=self.endOpenUrl, enterLinkCallback=self.setLinkLabel, leaveLinkCallback=self.setLinkLabel) self.help_text.grid(row=row, column=0, columnspan=3, sticky=Tkinter.NSEW) row = row + 1 self.link_label = Label(self) self.link_label.grid(row=row, column=0, columnspan=2, sticky=Tkinter.W) button = memops.gui.Util.createDismissButton(self, dismiss_cmd=self.close) button.grid(row=row, column=2) self.urls = [] self.current = -1 self.updateUrlList = True self.setButtonState() def close(self): if (self.popup): if (self.popup.modal): self.popup.do_grab() popup = self.parent while (not hasattr(popup, 'top')): popup = popup.parent popup.close() def createFindFrame(self, master): frame = Frame(master) frame.grid(row=0, column=0, sticky=Tkinter.W) arrow = ToggleArrow(frame, callback=self.toggleFindFrame) arrow.grid(row=0, column=0) button = Button(frame, text='find:', command=self.findPhrase) button.grid(row=0, column=1) self.find_entry = Entry(frame, width=20, returnCallback=self.findPhrase) self.find_entry.grid(row=0, column=2) self.find_frame = frame = Frame(master) entries = ('search forwards', 'search backwards') self.direction_buttons = PulldownMenu(frame, entries=entries) self.direction_buttons.grid(row=0, column=0, sticky=Tkinter.W, padx=5) self.forwards_entries = entries = ('wrap search', 'stop at end') self.backwards_entries = ('wrap search', 'stop at beginning') self.wrap_buttons = PulldownMenu(frame, entries=entries) self.wrap_buttons.grid(row=0, column=1, sticky=Tkinter.W, padx=5) self.direction_buttons.callback = self.setWrapText entries = ('case insensitive', 'case sensitive') self.case_buttons = PulldownMenu(frame, entries=entries) self.case_buttons.grid(row=0, column=2, sticky=Tkinter.W, padx=5) entries = ('literal search', 'regex search') self.pattern_buttons = PulldownMenu(frame, entries=entries) self.pattern_buttons.grid(row=0, column=3, sticky=Tkinter.W, padx=5) self.countVar = Tkinter.IntVar() def setWrapText(self, entry_ind, entry): if (entry_ind == 0): entries = self.forwards_entries else: entries = self.backwards_entries self.wrap_buttons.replace( entries, selected_index=self.wrap_buttons.getSelectedIndex()) def findPhrase(self, *extra): phrase = self.find_entry.get() if (not phrase): showError('No find phrase', 'Enter phrase in entry box.') return if (self.direction_buttons.getSelectedIndex() == 0): forwards = 1 backwards = 0 else: forwards = 0 backwards = 1 if (self.case_buttons.getSelectedIndex() == 0): nocase = 1 else: nocase = 0 if (self.pattern_buttons.getSelectedIndex() == 0): exact = 1 regexp = 0 else: exact = 0 regexp = 1 start = self.help_text.tag_nextrange(Tkinter.SEL, '1.0') if (start): start = start[0] if (forwards): start = '%s+1c' % start else: start = '%s-1c' % start else: start = Tkinter.CURRENT if (self.wrap_buttons.getSelectedIndex() == 0): match = self.help_text.search(phrase, start, count=self.countVar, forwards=forwards, backwards=backwards, nocase=nocase, exact=exact, regexp=regexp) elif (forwards): match = self.help_text.search(phrase, start, count=self.countVar, forwards=forwards, backwards=backwards, nocase=nocase, exact=exact, regexp=regexp, stopindex=Tkinter.END) else: match = self.help_text.search(phrase, start, count=self.countVar, forwards=forwards, backwards=backwards, nocase=nocase, exact=exact, regexp=regexp, stopindex='1.0') if (match): self.help_text.see(match) self.help_text.setSelection( match, '%s+%dc' % (match, self.countVar.get())) self.help_text.tag_config(Tkinter.SEL, background='gray70') else: showInfo('No match', 'No match found for phrase.') def toggleFindFrame(self, isClosed): if (isClosed): self.find_frame.grid_forget() else: self.find_frame.grid(row=1, column=0, sticky=Tkinter.W) def setButtonState(self): n = len(self.urls) if ((n >= 2) and (self.current > 0)): state = Tkinter.NORMAL else: state = Tkinter.DISABLED self.buttons.buttons[0].config(state=state) if ((n >= 2) and (self.current < (n - 1))): state = Tkinter.NORMAL else: state = Tkinter.DISABLED self.buttons.buttons[1].config(state=state) def setLinkLabel(self, url=''): self.link_label.set(url) def loadUrl(self, *extra): url = self.url_entry.get() if (url): if (url.find(':') == -1): if (url[:2] != '//'): url = '//' + url url = 'http:' + url self.showUrl(url, forceLoad=True) def newUrl(self, url): self.current = self.current + 1 self.urls[self.current:] = [[url, 0.0]] self.setButtonState() #print 'newUrl', self.current, self.urls def prevUrl(self): if (self.current > 0): # True if called via button (url, offset) = self.urls[self.current - 1] self.updateUrlList = False self.showUrl(url, offset=offset, allowModifyPath=False) self.updateUrlList = True self.current = self.current - 1 self.setButtonState() #print 'prevUrl', self.current, self.urls def nextUrl(self): if (self.current < (len(self.urls) - 1)): # True if called via button (url, offset) = self.urls[self.current + 1] self.updateUrlList = False self.showUrl(url, offset=offset, allowModifyPath=False) self.updateUrlList = True self.current = self.current + 1 self.setButtonState() #print 'nextUrl', self.current, self.urls def showText(self, message, popup=None): self.popup = popup if (popup): self.parent.modal = popup.modal self.help_text.setState(Tkinter.NORMAL) # so that can add text self.help_text.clear() # clear existing text self.help_text.append(message) self.help_text.setState(Tkinter.DISABLED) # make text read-only self.open() def startOpenUrl(self, yview): if (self.urls): self.urls[self.current][1] = yview[0] def endOpenUrl(self, url): #print 'endOpenUrl', url, self.updateUrlList self.url_entry.set(url) if (self.updateUrlList): self.newUrl(url) self.setLinkLabel() self.open() def showUrl(self, url, offset=None, forceLoad=False, allowModifyPath=True, popup=None): self.popup = popup if (popup): self.parent.modal = popup.modal self.help_text.openUrl(url, forceLoad=forceLoad, allowModifyPath=allowModifyPath) if (offset is not None): self.help_text.yview(Tkinter.MOVETO, str(offset))
class BlackledgeModuleFrame(Frame): """ Frame for handling MODULE calculation. Note that the frame uses (or creates) an NmrCalcStore named 'BLACKLEDGE_MODULE' and linked to the current NmrProject. """ def __init__(self, parent, project, closeButton=False, tempFiles=False, *args, **kw): ########################################################################### # INIT VARIABLES self.parent = parent self.project = project try: self.nmrProject = (project.currentNmrProject or project.newNmrProject(name='BLACKLEDGE_MODULE')) except: print '&&& Running MODULE popup from outside CCPN Analysis - debug only - no NmrCalc' self.nmrProject = None if self.nmrProject: self.calcStore = project.findFirstNmrCalcStore(name=MODULE, nmrProject=self.nmrProject) or \ project.newNmrCalcStore(name=MODULE, nmrProject=self.nmrProject) else: self.calcStore = None self.run = None self.inputStructure = None self.inputRdcConstraintList = None self.inputDistanceConstraintList = [None] self.inputUserDescriptionText = None # path to the module executable modPath = subprocess.Popen( ['which', 'module'], stdout=subprocess.PIPE).communicate()[0].strip() self.moduleExePath = modPath or ' NB. MODULE executable not found ' self.waiting = False # for debug this could be False if tempFiles: self.useTempFiles = True else: self.useTempFiles = False # create temp files for MODULE if self.useTempFiles: self.moduleTempDir = tempfile.mkdtemp(prefix='MODULE-') else: self.moduleTempDir = os.getcwd() #djo35# self.calcStore = self.resetCalcStore(name='BLACKLEDGE_MODULE') # END INIT OF VARIABLES ########################################################################### ########################################################################### # START GUI CODE Frame.__init__(self, parent, *args, **kw) self.expandGrid(0, 0) ## Single Frame # frame = Frame(self, grid=(0,0)) # or with Tabs? options = ['Launch', 'Output', 'Runs'] tabbedFrame = TabbedFrame(self, options=options, grid=(0, 0)) frameA, frameB, frameC = tabbedFrame.frames self.tabbedFrame = tabbedFrame frameA.expandGrid(14, 2) row = 0 div = LabelDivider(frameA, text='MODULE Setup', grid=(row, 0), gridSpan=(1, 4)) row += 1 # allow the user to choose MODULE if either the one in PATH is incorrect or not found button = Button(frameA, text='Select MODULE executable:',bd=1, \ command=self.selectExecutable, grid=(row,0), sticky="ew") self.moduleExeEntry = Entry(frameA, text=self.moduleExePath, grid=(row,1), gridSpan=(1,3), \ width=32, sticky="ew", bd=1) self.moduleExePath = self.moduleExeEntry.get() # separator "MODULE input" row += 1 div = LabelDivider(frameA, text='MODULE input', grid=(row, 0), gridSpan=(1, 5)) row += 1 label = Label(frameA, text='Structure:', grid=(row, 1)) self.inputStructurePulldown = PulldownList(frameA, self.changeInputStructure, \ grid=(row,2)) # self.constraintsFileEntry.bind('<Leave>', self.updateEntryParams) row += 1 label = Label(frameA, text='RDC constraints:', grid=(row, 1)) self.inputRdcConstraintsPulldown = PulldownList(frameA, self.changeInputRdcConstraintList, \ grid=(row,2)) #self.constraintsFileEntry.bind('<Leave>', self.updateEntryParams) row += 1 label = Label(frameA, text='(Optional input)', grid=(row, 0)) label = Label(frameA, text='Distance constraints:', \ grid=(row,1)) self.inputDistanceConstraintsPulldown = PulldownList(frameA, self.changeInputDistanceConstraintList, \ grid=(row,2)) #self.constraintsFileEntry.bind('<Leave>', self.updateEntryParams) row += 1 subFrameDepth = 4 subframe = LabelFrame(frameA, text='MODULE User Notes (store notes about how MODULE was run here)', \ grid=(row,0), gridSpan=(1,4)) subframe.expandGrid(subFrameDepth, 0) self.moduleUserText = ScrolledText(subframe) self.moduleUserText.grid(row=subFrameDepth, column=0, columnspan=4, sticky='nsew') # View Results row += subFrameDepth # row += 1 # div = LabelDivider(frameA, text='MODULE launch', grid=(row,0), gridSpan=(1,4)) row += 1 button = Button(frameA, text='Run MODULE', bd=1, command=self.executeModule, \ grid=(row,0), gridSpan=(1,4), sticky="ew", bg=MODULE_GREEN) # grid=(row,0), gridSpan=(1,2), sticky="ew", bg=MODULE_GREEN) ########################################################################### # Frame B (tab 2) Ouput frameB.expandGrid(4, 1) row = 0 subFrameDepth = 6 subframe = LabelFrame(frameB, text='MODULE Output', \ grid=(row,0), gridSpan=(1,5)) #subframe.grid_columnconfigure(2, weight=1) subframe.expandGrid(subFrameDepth, 0) self.moduleOutputText = ScrolledText(subframe) self.moduleOutputText.setState(state=Tkinter.DISABLED) self.moduleOutputText.grid(row=subFrameDepth, column=0, columnspan=4, sticky='nsew') # separator "MODULE input" row += 1 div = LabelDivider(frameB, text='MODULE RDC Back Values', grid=(row, 0), gridSpan=(1, 5)) row += 1 button = Button(frameB, text='Import MODULE Back Values file', bd=1, command=self.importModuleBackValues, \ grid=(row,0), gridSpan=(1,4), sticky="ew", bg=MODULE_BLUE) # grid=(row,0), gridSpan=(2,4), sticky="ew", bg=MODULE_BLUE) row += 1 self.rdcOutputTable = None frameB.grid_rowconfigure(row, weight=1) headings = ('#', 'Resonances', 'Value', 'Back Value', 'Diff.', 'Error') editWidgets = [None, None, None, None, None, None] editGetCallbacks = [None, None, None, None, None, None] editSetCallbacks = [None, None, None, None, None, None] self.rdcOutputTable = ScrolledMatrix(frameB, headingList=headings, multiSelect=False, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, initialRows=4) self.rdcOutputTable.grid(row=row, column=0, columnspan=4, sticky='nsew') row += 1 button = Button(frameB, text='Import MODULE Structure', bd=1, command=self.importModuleStructure, \ grid=(row,0), gridSpan=(1,4), sticky="ew", bg=MODULE_BLUE) # grid=(row,0), gridSpan=(2,4), sticky="ew", bg=MODULE_BLUE) ########################################################################### # Frame C (tab 3) NMR Calc display bits frameC.expandGrid(4, 1) row = 0 div = LabelDivider(frameC, text='Stored MODULE Runs', grid=(row, 0), gridSpan=(1, 5)) # NmrCalc Run scrolled matrix row += 1 self.runTable = None frameC.grid_rowconfigure(row, weight=1) headings = ('Run ID', 'notes', 'Status') # self.editRunNotes = DataEntry.askString('Run Notes', 'Edit notes about Run', tipText='Notes about Run', parent=self) # editWidgets = [None, self.editRunNotes, None] editWidgets = [None, None, None] editGetCallbacks = [None, None, None] editSetCallbacks = [None, None, None] self.runTable = ScrolledMatrix(frameC, headingList=headings, multiSelect=False, editWidgets=editWidgets, editGetCallbacks=editGetCallbacks, editSetCallbacks=editSetCallbacks, initialRows=4) self.runTable.grid(row=row, column=0, columnspan=4, sticky='nsew') row += 4 tipTexts = ['Load Selected Run', 'Delete Selected Run'] texts = ['Load Selected Run', 'Delete Selected'] commands = [self.loadRun, self.deleteRun] colours = [MODULE_GREEN, MODULE_RED] self.runButtons = ButtonList(frameC, texts=texts, tipTexts=tipTexts, commands=commands, grid=(row, 0), gridSpan=(1, 4)) self.runButtons.buttons[0].config(bg=MODULE_GREEN) self.runButtons.buttons[1].config(bg=MODULE_RED) ########################################################################### # Keep GUI up to date self.updateAfter() self.administerNotifiers(self.parent.registerNotify) # END GUI CODE ########################################################################### ########################################################################### # FUNCTIONS CALLED FROM GUI ################ # OS interaction def selectExecutable(self): """ Choose the MODULE executable appropriate for your system. """ popup = FileSelectPopup(self, show_file=True) executable = popup.getFile() if executable: self.moduleExeEntry.set(executable) popup.destroy() def executeModule(self): """ Execute MODULE with all given parameters Yeah it's a big function, bite me :-P """ # check that all params have been set if not os.path.isfile(self.moduleExePath): popup = showWarning('MODULE', 'Cannot find MODULE executable.', parent=self) return if self.inputStructure is None: popup = showWarning( 'MODULE', 'MODULE cannot run with out a structure for input.', parent=self) return if self.inputRdcConstraintList is None: popup = showWarning( 'MODULE', 'MODULE cannot run with out an RDC constraint list for input.', parent=self) return # write temp PDB file strucTempFile = tempfile.NamedTemporaryFile(mode='w', suffix='.pdb', dir=self.moduleTempDir, delete=False) strucTempFile.close() makePdbFromStructure(strucTempFile.name, self.inputStructure.structureEnsemble, model=self.inputStructure) # write temp RDC file rdcTempFile = tempfile.NamedTemporaryFile(mode='w', suffix='.tab', dir=self.moduleTempDir, delete=False) moduleIo.writeConstraintList(rdcTempFile, self.inputRdcConstraintList) rdcTempFile.close() # command to be executed (to start MODULE) moduleCommands = [ self.moduleExePath, strucTempFile.name, rdcTempFile.name ] # if user has set Distance constraint list then fill add this (user optional) if self.inputDistanceConstraintList: distanceTempFile = tempfile.NamedTemporaryFile( mode='w', suffix='.tab', dir=self.moduleTempDir, delete=False) moduleIo.writeConstraintList(distanceTempFile, self.inputDistanceConstraintList) distanceTempFile.close() moduleCommands.append(distanceTempFile.name) # execute MODULE moduleOutput = subprocess.Popen( moduleCommands, stdout=subprocess.PIPE).communicate()[0] # clean out the blank lines for line in moduleOutput: if line == '': moduleOutput.pop(line) # send the output to our screen self.moduleOutputText.setState(state=Tkinter.NORMAL) self.moduleOutputText.setText(text=moduleOutput) self.moduleOutputText.setState(state=Tkinter.DISABLED) # clean up temp files (delete has been set to False) for tempFile in [strucTempFile.name, rdcTempFile.name]: if os.path.isfile(tempFile): os.unlink(tempFile) if self.inputDistanceConstraintList: if os.path.isfile(distanceTempFile.name): os.unlink(distanceTempFile.name) # create new run and store inputs self.newRun() self.updateRunInputs() ############################################################################ # Outputs: ############################ # Import MODULE PDB def findModuleExportPdbFile(self): """ Find the MODULE PDB export file, typically called temp%s.pdb unless user has renamed it. """ modulePdbFileGood = False def yes(): modulePdbFileGood = True def cancel(): modulePdbFileGood = False # MODULE writes pdb files to CWD no matter where you or it is possibleFiles = glob.glob(os.path.join(os.getcwd(), 'temp*')) # debug # possibleFiles = glob.glob( os.path.join( os.getcwd(), 'module', 'temp*' ) ) if len(possibleFiles) == 1: if os.path.isfile(possibleFiles[0]): texts = ['Yes', 'No, choose another PDB file', 'Cancel'] objects = [yes, self.selectModulePdbExport, cancel] func = showMulti('MODULE', 'Is this the PDB strcture you exported from MODULE?\n%s' % possibleFiles[0], \ texts=texts, objects=objects, parent=self) func() if modulePdbFileGood == True: modulePdbFile = possibleFiles[0] else: return None else: modulePdbFile = self.selectModulePdbExport() elif len(possibleFiles) > 1: texts = ['%s' % fileName for fileName in possibleFiles] objects = ['%s' % fileName for fileName in possibleFiles] modulePdbFile = showMulti('MODULE', 'Multiple possible MODULE export\nfiles found, please select one:' % possibleFiles, \ texts=texts, objects=objects, parent=self) if os.path.isfile(modulePdbFile): modulePdbFile = modulePdbFile else: modulePdbFile = self.selectModulePdbExport() return modulePdbFile def selectModulePdbExport(self): """ Choose the PDB file that was exported from MODULE. """ file_types = [ FileType("PDB structure files", ["*.pdb"]), FileType("All files", ["*"]) ] popup = FileSelectPopup(self, file_types, dismiss_text='Cancel', show_file=True) chosenPdbFile = popup.getFile() if os.path.isfile(chosenPdbFile): modulePdbFile = chosenPdbFile else: warnPopup = showWarning('MODULE', 'File %s not found.' % chosenPdbFile, parent=self) return None popup.destroy() return modulePdbFile def importModuleStructure(self): """ Find MODULE structure file and import into Analysis (or attempt to, this will probably barf as MODULE pdb files are not good) """ modulePdbFile = self.findModuleExportPdbFile() if not modulePdbFile: return # this stores the re-hacked MODULE pdb file and then removes it on close tempPdb = tempfile.NamedTemporaryFile(mode='w', suffix='.pdb', dir=self.moduleTempDir, delete=False) tempPdb.close() # if file found then add to project if os.path.isfile(modulePdbFile): moduleIo.BlackledgeToPdbConverter(modulePdbFile, tempPdb) structure = getStructureFromFile( self.inputStructure.parent.molSystem, tempPdb) os.unlink(tempPdb) ############################ # Import MODULE Back Values def findModuleExportBackValuesFile(self): """ Find the Back Values file that the User hopefully exported from MODULE """ def yes(): moduleBvFileGood = True def cancel(): moduleBvFileGood = False # back value files are helpfully appended '*.back' possibleFiles = glob.glob(os.path.join(os.getcwd(), '*.back')) possibleFiles += glob.glob(os.path.join(self.moduleTempDir, '*.back')) if len(possibleFiles) == 1: if os.path.isfile(possibleFiles[0]): texts = [ 'Yes', 'No, choose another Back Values file', 'Cancel' ] objects = [yes, self.selectModuleBvExport, cancel] func = showMulti('MODULE', 'Is this the Back Values file that you exported from MODULE?\n%s' % possibleFiles[0], \ texts=texts, objects=objects, parent=self) func() if moduleBvFileGood == True: moduleBackValueFile = possibleFiles[0] else: return None else: moduleBackValueFile = self.selectModuleBvExport() elif len(possibleFiles) > 1: texts = ['%s' % fileName for fileName in possibleFiles] objects = ['%s' % fileName for fileName in possibleFiles] chosenBvFile = showMulti('MODULE', 'Multiple possible MODULE Back Value\nfiles found, please select one:' % possibleFiles, \ texts=texts, objects=objects, parent=self) if os.path.isfile(modulePdbFile): moduleBackValueFile = chosenBvFile else: moduleBackValueFile = self.selectModuleBvExport() return moduleBackValueFile def selectModuleBvExport(self): """ Choose the Back Value file that was exported from MODULE. """ file_types = [ FileType("Back Value files", ["*.back"]), FileType("All files", ["*"]) ] popup = FileSelectPopup(self, file_types, dismiss_text='Cancel', show_file=True) chosenBvFile = popup.getFile() if os.path.isfile(chosenBvFile): moduleBackValueFile = chosenBvFile else: warnPopup = showWarning('MODULE', 'File %s not found.' % chosenBvFile, parent=self) return None popup.destroy() return moduleBackValueFile def importModuleBackValues(self): """ Attempt to find the Back Values file and then import it ... """ # find the Back Values file or ask user backValFile = self.findModuleExportBackValuesFile() if not backValFile: return # create a new RDC list if self.project: nmrConstraintStore = self.project.newNmrConstraintStore( nmrProject=self.project.findFirstNmrProject()) if self.inputStructure: chain = self.inputStructure.parent.molSystem.findFirstChain() else: chain = None if nmrConstraintStore and chain: rdcList = moduleIo.getBackValuesListFromFile( backValFile, chain, nmrConstraintStore) # rdcRawData = moduleIo.getRawBackValuesFromFile( backValFile ) # # for run in runs: # runText.append( [run.getSerial(), run.getDetails(), run.getStatus()] ) self.rdcOutputTable.update(objectList=None, textMatrix=None) # end OS interaction #################### ################################## # get, set and update menu options def changeInputRun(self, run): self.run = run def changeInputStructure(self, structure): self.inputStructure = structure def changeInputRdcConstraintList(self, constraintList): self.inputRdcConstraintList = constraintList def changeInputDistanceConstraintList(self, constraintList): self.inputDistanceConstraintList = constraintList def changeInputConstraintList(self, constraintList, className): if className == 'RdcConstraintList': self.changeInputRdcConstraintList(constraintList) elif className == 'DistanceConstraintList': self.changeInputDistanceConstraintList(constraintList) def getInputConstraintList(self, className): if className == 'RdcConstraintList': return self.inputRdcConstraintList elif className == 'DistanceConstraintList': return self.inputDistanceConstraintList # end get and set ################################## #################### # NMR Calc runs etc. def loadRun(self): pass def deleteRun(self): pass def changeRun(self): pass def newRun(self): if self.calcStore: self.run = self.calcStore.newRun(status='provisional') def updateRunInputs(self, event=None): self.inputUserDescriptionText = self.moduleUserText.getText() or None # store input parameters in run if self.run: # set the path to the MODULE executable used setRunTextParameter(self.run, MODULE_EXE, self.moduleExePath) # set the input structure sent to MODULE sEnsemble = self.inputStructure.StructureEnsemble self.run.newStructureEnsembleData(sEnsemble.ensembleId, sEnsemble.molSystem.code, name=STRUCTURE_DATA, ioRole=INPUT) # set the input RDC constraint list rdcConst = self.inputRdcConstraintList self.run.newConstraintStoreData(rdcConst.nmrConstraintStore.serial, name=RDC_CONSTRAINT_DATA, ioRole=INPUT) # set the input RDC constraint list (optional) distConst = self.inputDistanceConstraintList if distConst: self.run.newConstraintStoreData( distConst.nmrConstraintStore.serial, name=DIST_CONSTRAINT_DATA, ioRole=INPUT) # set the input user data (if any) setRunTextParameter(self.run, USER_DESCRIPTION_DATA, self.inputUserDescriptionText) def editRunNotes(self): pass # end NMR calc stuff #################### ###################### # update and noitfiers def administerNotifiers(self, notifyFunc): for func in ['__init__', 'delete']: for clazz in ['RdcConstraintList', 'DistanceConstraintList']: notifyFunc(self.updateAfter, 'ccp.nmr.NmrConstraint.' + clazz, func) notifyFunc(self.updateAfter, 'ccp.nmr.NmrCalc.Run', func) def destroy(self): self.administerNotifiers(self.parent.unregisterNotify) Frame.destroy(self) def updateAll(self): # tab A self.updateModuleExecutablePath() self.updateInputStructure() self.updateInputRdcConstraintList() self.updateInputDistanceConstraintList() # tab C self.updateInputRuns() self.waiting = False def updateAfter(self, obj=None): if self.waiting: return else: self.waiting = True self.after_idle(self.updateAll) def updateInputRuns(self): if self.calcStore: runs = [ r for r in self.calcStore.sortedRuns() if r.status == PROVISIONAL ] if not runs: self.newRun() runs = [self.run] runText = [] for run in runs: runText.append( [run.getSerial(), run.getDetails(), run.getStatus()]) self.runTable.update(objectList=runs, textMatrix=runText) def updateModuleExecutablePath(self): self.moduleExePath = self.moduleExeEntry.get() def updateInputStructure(self): index = None # names = ['<None>'] # structures = [None] names = [] structures = [] for molSystem in self.project.sortedMolSystems(): for ensemble in molSystem.sortedStructureEnsembles(): for structure in ensemble.sortedModels(): structures.append(structure) if structures: for i, model in enumerate(structures): if model is None: continue ee = model.structureEnsemble name = '%s:%d:%d' % (ee.molSystem.code, ee.ensembleId, model.serial) names.append(name) if self.inputStructure not in structures: self.changeInputStructure(structures[0]) index = structures.index(self.inputStructure) else: self.inputStructure = None self.inputStructurePulldown.setup(names, structures, index or -1) def updateInputConstraintList(self, className, obj=None): index = None # if DistanceConstraintList then None must be an option # names = ['<None>'] # constraintLists = [None] if className == 'DistanceConstraintList': names = ['<None>'] constraintLists = [None] else: names = [] constraintLists = [] for nmrConstraintStore in self.parent.project.sortedNmrConstraintStores( ): for constraintList in nmrConstraintStore.sortedConstraintLists(): if not className or constraintList.className == className: constraintLists.append(constraintList) if constraintLists: for constList in constraintLists: if constList is None: continue store = constList.nmrConstraintStore name = '%d:%d' % (store.serial, constList.serial) names.append(name) if self.getInputConstraintList(className) not in constraintLists: self.changeInputConstraintList(constraintLists[0], className) index = constraintLists.index( self.getInputConstraintList(className)) else: self.changeInputConstraintList(None, className) if className == 'RdcConstraintList': self.inputRdcConstraintsPulldown.setup(names, constraintLists, index or -1) elif className == 'DistanceConstraintList': self.inputDistanceConstraintsPulldown.setup( names, constraintLists, index or -1) def updateInputRdcConstraintList(self): self.updateInputConstraintList('RdcConstraintList') def updateInputDistanceConstraintList(self): self.updateInputConstraintList('DistanceConstraintList')
class EditExperimentSeriesPopup(BasePopup): """ **Setup Experiment Series for Chemical Shift and Intensity Changes** The purpose of this popup is to setup ordered groups of experiments that are related by the variation in some condition or parameter, but otherwise the kind of experiment being run is the same. For example the user could setup experiments for a temperature series, ligand binding titration or relaxation rate measurement. The layout is divided into two tables. The upper table shows all of the series that are known to the current project and the lower table shows all of the experiments or planes that comprise the selected series. These series relate to both groups of separate experiments (and hence spectra) and also single experiment where one dimension is not for NMR frequency but rather a "sampled" dimension and thus effectively combines many experiments (e.g. for different T1 values) as different planes. A stack of effectively 2D experiments combined in this manner is typically referred to as pseudo-3D. - The experiment is 3D but there are only two NMR dimensions. Series which are stacks of planes in a single experiment entity are automatically detected and entered into the table once they are loaded. Series that are constructed from multiple, separate experiments however must be setup by the user. To setup a new series of this kind [Add Series] makes a new, empty NMR series. Next the user should change the "Parameter varied" column to specify what type of thing is varied between the different experiments. The user then adds experiments into this series with the [Add Series Point] function at the bottom. Once the points have been added to the series the name of the experiment for each point may be changed. Initially, arbitrary experiments appear for the series points, so these almost always have to be adjusted. Once a stacked-plane experiment or series of experiments is setup, the user next sets (or checks) the value of the parameter associated with each point in the lower table. When loading stacked-plane experiments these values may come though automatically, if they are present in the spectrum header or parameter file. Given a completed NMR series specification the user may then extract the relevant data from the series using one of the analysis tools like `Follow Intensity Changes`_ or `Follow Shift Changes`_. **Caveats & Tips** Make sure the "Parameter Varied" for a given NMR series is appropriate to the type of analysis being performed. Many tools that extract T1 or Kd measurements for example look for specific types of series. The "Set/Unset Ref Plane" function is only used in certain kinds of series such as those that use trains of CPMG pulses. .. _`Follow Intensity Changes`: CalcRatesPopup.html .. _`Follow Shift Changes`: FollowShiftChangesPopup.html """ def __init__(self, parent, *args, **kw): self.guiParent = parent self.expSeries = None self.conditionPoint = None self.waiting = 0 BasePopup.__init__(self, parent, title="Experiment : NMR Series", **kw) def body(self, guiFrame): self.geometry("500x500") self.nameEntry = Entry(self, text='', returnCallback=self.setName, width=12) self.detailsEntry = Entry(self, text='', returnCallback=self.setDetails, width=16) self.valueEntry = FloatEntry(self, text='', returnCallback=self.setValue, width=10) self.errorEntry = FloatEntry(self, text='', returnCallback=self.setError, width=8) self.conditionNamesPulldown = PulldownList(self, callback=self.setConditionName, texts=self.getConditionNames()) self.unitPulldown = PulldownList(self, callback=self.setUnit, texts=self.getUnits()) self.experimentPulldown = PulldownList(self, callback=self.setExperiment) guiFrame.grid_columnconfigure(0, weight=1) row = 0 frame = Frame(guiFrame, grid=(row, 0)) frame.expandGrid(None,0) div = LabelDivider(frame, text='Current Series', grid=(0, 0)) utilButtons = UtilityButtonList(frame, helpUrl=self.help_url, grid=(0,1)) row += 1 frame0 = Frame(guiFrame, grid=(row, 0)) frame0.expandGrid(0,0) tipTexts = ['The serial number of the experiment series, but left blank if the series as actually a pseudo-nD experiment (with a sampled non-frequency axis)', 'The name of the experiment series, which may be a single pseudo-nD experiment', 'The number of separate experiments (and hence spectra) present in the series', 'The kind of quantity that varies for different experiments/planes within the NMR series, e.g. delay time, temperature, ligand concentration etc.', 'The number of separate points, each with a separate experiment/plane and parameter value, in the series'] headingList = ['#','Name','Experiments','Parameter\nVaried','Num\nPoints'] editWidgets = [None, self.nameEntry, None, self.conditionNamesPulldown, None] editGetCallbacks = [None, self.getName, None, self.getConditionName, None] editSetCallbacks = [None, self.setName, None, self.setConditionName, None] self.seriesMatrix = ScrolledMatrix(frame0, tipTexts=tipTexts, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, headingList=headingList, callback=self.selectExpSeries, deleteFunc=self.deleteExpSeries, grid=(0,0), gridSpan=(None, 3)) tipTexts = ['Make a new, blank NMR series specification in the CCPN project', 'Delete the selected NMR series from the project, although any component experiments remain. Note you cannot delete pseudo-nD series; delete the actual experiment instead', 'Colour the spectrum contours for each experiment in the selected series (not pseudo-nD) using a specified scheme'] texts = ['Add Series','Delete Series', 'Auto Colour Spectra'] commands = [self.addExpSeries,self.deleteExpSeries, self.autoColorSpectra] self.seriesButtons = ButtonList(frame0, texts=texts, commands=commands, grid=(1,0), tipTexts=tipTexts) label = Label(frame0, text='Scheme:', grid=(1,1)) tipText = 'Selects which colour scheme to apply to the contours of (separate) experiments within an NMR series' self.colorSchemePulldown = PulldownList(frame0, grid=(1,2), tipText=tipText) row += 1 div = LabelDivider(guiFrame, text='Experimental Parameters & Conditions', grid=(row, 0)) row += 1 guiFrame.grid_rowconfigure(row, weight=1) frame1 = Frame(guiFrame, grid=(row, 0)) frame1.expandGrid(0,0) tipTexts = ['The kind of experimental parameter that is being used to define the NMR series', 'The experiment that corresponds to the specified parameter value; can be edited from an arbitrary initial experiment', 'The numeric value of the parameter (condition) that relates to the experiment or point in the NMR series', 'The estimated error in value of the condition', 'The measurement unit in which the value of the condition is represented'] headingList = ['Parameter','Experiment','Value','Error','Unit'] editWidgets = [None,self.experimentPulldown,self.valueEntry,self.errorEntry, self.unitPulldown] editGetCallbacks = [None,self.getExperiment, self.getValue, self.getError, self.getUnit] editSetCallbacks = [None,self.setExperiment, self.setValue, self.setError, self.setUnit] self.conditionPointsMatrix = ScrolledMatrix(frame1, grid=(0,0), tipTexts=tipTexts, editSetCallbacks=editSetCallbacks, editGetCallbacks=editGetCallbacks, editWidgets=editWidgets, headingList=headingList, callback=self.selectConditionPoint, deleteFunc=self.deleteConditionPoint) self.conditionPointsMatrix.doEditMarkExtraRules = self.conditionTableShow tipTexts = ['Add a new point to the NMR series with an associated parameter value and experiment', 'Remove the selected point from the series, including any associated parameter value', 'For appropriate kinds of NMR series, set or unset a point as representing the plane to use as a reference'] texts = ['Add Series Point','Delete Series Point','Set/Unset Ref Plane'] commands = [self.addConditionPoint,self.deleteConditionPoint,self.setSampledReferencePlane] self.conditionPointsButtons = ButtonList(frame1, texts=texts, commands=commands, tipTexts=tipTexts, grid=(1,0)) self.updateAfter() self.updateColorSchemes() self.administerNotifiers(self.registerNotify) def administerNotifiers(self, notifyFunc): #for func in ('__init__', 'delete','setName'): for func in ('__init__', 'delete','setName','setConditionNames', 'addConditionName','removeConditionName'): notifyFunc(self.updateAfter,'ccp.nmr.Nmr.NmrExpSeries', func) for func in ('__init__', 'delete','setName'): notifyFunc(self.updateExperiments,'ccp.nmr.Nmr.Experiment', func) for func in ('__init__', 'delete'): notifyFunc(self.updateDataDim,'ccp.nmr.Nmr.SampledDataDim', func) for func in ('__init__', 'delete','setCondition','setUnit','setValue','setError'): notifyFunc(self.updateConditionsAfter,'ccp.nmr.Nmr.SampleCondition', func) for func in ('__init__', 'delete','setCondition'): notifyFunc(self.updateAfter,'ccp.nmr.Nmr.SampleCondition', func) for func in ('setConditionVaried', 'setPointErrors', 'addPointError', 'removePointError', 'setPointValues','addPointValue', 'removePointValue','setUnit'): notifyFunc(self.updateAfter,'ccp.nmr.Nmr.SampledDataDim', func) for func in ('__init__', 'delete'): notifyFunc(self.updateColorSchemes,'ccpnmr.AnalysisProfile.ColorScheme', func) def open(self): self.updateAfter() BasePopup.open(self) def updateColorSchemes(self, scheme=None): index = 0 prevScheme = self.colorSchemePulldown.getObject() schemes = getHueSortedColorSchemes(self.analysisProfile) schemes = [s for s in schemes if len(s.colors) > 1] colors = [list(s.colors) for s in schemes] if schemes: names = [s.name for s in schemes] if prevScheme in schemes: index = schemes.index(prevScheme) else: names = [] self.colorSchemePulldown.setup(names, schemes, index, colors) def autoColorSpectra(self): if self.expSeries and (self.expSeries.className != 'Experiment'): scheme = self.colorSchemePulldown.getObject() if scheme: colors = scheme.colors else: colors = ['#FF0000','#00FF00','#0000FF'] cdict = getNmrExpSeriesSampleConditions(self.expSeries) conditionName = list(self.expSeries.conditionNames)[0] expList = [] for sampleCondition in cdict.get(conditionName, []): expList.append( (sampleCondition.value, sampleCondition.parent.experiments) ) expList.sort() m = len(expList)-1.0 c = len(colors)-1 for i, (value, experiments) in enumerate(expList): p = c*i/m j = int(p) r1, g1, b1 = Color.hexToRgb(colors[j]) r2, g2, b2 = Color.hexToRgb(colors[min(c,j+1)]) f2 = p-j f1 = 1.0-f2 r = (r1*f1)+(r2*f2) g = (g1*f1)+(g2*f2) b = (b1*f1)+(b2*f2) hexColor = Color.hexRepr(r,g,b) for experiment in experiments: for spectrum in experiment.dataSources: if spectrum.dataType == 'processed': analysisSpec = getAnalysisSpectrum(spectrum) if analysisSpec.posColors: analysisSpec.posColors = [hexColor,] elif analysisSpec.negColors: analysisSpec.negColors = [hexColor,] def getUnusedExperiments(self): sampleExperiments = getSampledDimExperiments(self.nmrProject) experiments = [] for experiment in self.nmrProject.sortedExperiments(): if experiment in sampleExperiments: continue if self.expSeries and (self.expSeries.className != 'Experiment'): if experiment in self.expSeries.experiments: continue experiments.append(experiment) return experiments def conditionTableShow(self, object, row, col): if type(object) is type(()): dataDim, index = object refPlane = dataDim.analysisDataDim.refSamplePlane if refPlane == index: return False if col == 1: return False return True def setSampledReferencePlane(self): if self.expSeries and (self.expSeries.className == 'Experiment'): if self.conditionPoint: dataDim, point = self.conditionPoint analysisDataDim = dataDim.analysisDataDim refPoint = analysisDataDim.refSamplePlane if refPoint == point: analysisDataDim.refSamplePlane = None else: analysisDataDim.refSamplePlane = point self.updateAfter() def checkAddSampleCondition(self, experiment): conditionSet = getExperimentConditionSet(experiment) conditionName = self.expSeries.conditionNames[0] condDict = getNmrExpSeriesSampleConditions(self.expSeries) sampleConditions = condDict.get(conditionName, []) units = [sc.unit for sc in sampleConditions if sc.unit] if units: sortList = list(set(units)) sortList.sort(key = lambda u:units.count(u)) unit = sortList[-1] else: unit = CONDITION_UNITS_DICT[conditionName][0] condition = conditionSet.findFirstSampleCondition(condition=conditionName) if not condition: condition = conditionSet.newSampleCondition(condition=conditionName, unit=unit, value=0.0, error=0.0) def addConditionPoint(self): if self.expSeries and (self.expSeries.className != 'Experiment'): experiments = self.getUnusedExperiments() if not experiments: showWarning('Warning','No experiments available', parent=self) return experiment = experiments[0] if experiment not in self.expSeries.experiments: self.expSeries.addExperiment(experiment) self.checkAddSampleCondition(experiment) self.updateAfter() def deleteConditionPoint(self, *event): if self.conditionPoint and (self.expSeries.className != 'Experiment'): if showOkCancel('Confirm','Really delete series point?', parent=self): conditionSet = self.conditionPoint.sampleConditionSet experiments = [e for e in conditionSet.experiments if e in self.expSeries.experiments] for experiment in experiments: self.expSeries.removeExperiment(experiment) for experiment in experiments: for expSeries in experiment.nmrExpSeries: if self.conditionPoint.condition in self.expSeries.conditionNames: break else: continue break else: self.conditionPoint.delete() self.conditionPoint = None def selectConditionPoint(self, object, row, col): if object: self.conditionPoint = object self.updateButtons() def selectExpSeries(self, object, row, col): if object: self.expSeries = object self.checkExperimentConditionsConsistent() self.updateConditions() def checkExperimentConditionsConsistent(self): if self.expSeries.className != 'Experiment': for experiment in self.expSeries.experiments: self.checkAddSampleCondition(experiment) def getUnits(self): units = [] if self.expSeries: if self.expSeries.className == 'Experiment': conditionName = getExperimentSampledDim(self.expSeries).conditionVaried else: conditionName = self.expSeries.conditionNames[0] units = CONDITION_UNITS_DICT.get(conditionName) if not units: units = ['?',] return units def getUnit(self, sampleCondition): index = -1 units = self.getUnits() if units: if sampleCondition: if type(sampleCondition) is type(()): dataDim, index = sampleCondition unit = dataDim.unit else: unit = sampleCondition.unit if unit not in units: unit = units[0] index = units.index(unit) self.unitPulldown.setup(units,units,index) def setUnit(self, obj): name = self.unitPulldown.getObject() if self.conditionPoint: if type(self.conditionPoint) is type(()): dataDim, index = self.conditionPoint dataDim.setUnit(name) else: self.conditionPoint.setUnit(name) def getConditionNames(self): if self.expSeries and (self.expSeries.className == 'Experiment'): names = ['delay time','mixing time','num delays','pulsing frequency','gradient strength'] else: names = CONDITION_UNITS_DICT.keys() names.sort() return names def setConditionName(self, obj): name = self.conditionNamesPulldown.getObject() if self.expSeries: if self.expSeries.className == 'Experiment': dataDim = getExperimentSampledDim(self.expSeries) dataDim.conditionVaried = name else: self.expSeries.setConditionNames([name,]) def getConditionName(self, expSeries): index = 0 names = self.getConditionNames() if names: if expSeries: if expSeries.className == 'Experiment': name = getExperimentSampledDim(expSeries).conditionVaried else: name = expSeries.conditionNames[0] if name: index = names.index(name) else: index = 0 self.conditionNamesPulldown.setup(names,names,index) def getName(self, expSeries): if expSeries : self.nameEntry.set(expSeries.name) def setName(self, event): text = self.nameEntry.get() if text and text != ' ': self.expSeries.setName( text ) def getValue(self, conditionPoint): if conditionPoint: if type(self.conditionPoint) is type(()): dataDim, index = conditionPoint value = dataDim.pointValues[index] else: value = conditionPoint.value self.valueEntry.set(value) def setValue(self, event): value = self.valueEntry.get() if value is not None: if type(self.conditionPoint) is type(()): dataDim, index = self.conditionPoint values = list(dataDim.pointValues) values[index] = value dataDim.setPointValues(values) else: self.conditionPoint.setValue( value ) def getError(self, conditionPoint): if conditionPoint: if type(self.conditionPoint) is type(()): dataDim, index = conditionPoint if index < len(dataDim.pointErrors): error = dataDim.pointValues[index] else: error = 0.0 else: error = conditionPoint.error self.errorEntry.set(error) def setError(self, event): value = self.errorEntry.get() if value is not None: if type(self.conditionPoint) is type(()): dataDim, index = self.conditionPoint pointErrors = dataDim.pointErrors if pointErrors: values = list(pointErrors) else: values = [0.0] * dataDim.numPoints values[index] = value dataDim.setPointErrors(values) else: self.conditionPoint.setError( value ) def getDetails(self, expSeries): if expSeries : self.detailsEntry.set(expSeries.details) def setDetails(self, event): text = self.detailsEntry.get() if text and text != ' ': self.expSeries.setDetails( text ) def addExpSeries(self): expSeries = self.nmrProject.newNmrExpSeries(conditionNames=['delay time',]) def deleteExpSeries(self, *event): if self.expSeries and (self.expSeries.className != 'Experiment'): if showOkCancel('Confirm','Really delete series?', parent=self): self.expSeries.delete() self.expSeries = None self.conditionPoint = None def getExperiments(self): if self.expSeries and (self.expSeries.className == 'Experiment'): return [self.expSeries,] else: return self.nmrProject.sortedExperiments() def getExperiment(self, sampleCondition): index = 0 names = [] if self.conditionPoint and (type(self.conditionPoint) != type(())): index = 0 experiment = self.conditionPoint.parent.findFirstExperiment() experiments = self.getUnusedExperiments() name = experiment.name names = [name] + [e.name for e in experiments] experiments = [experiment,] + experiments self.experimentPulldown.setup(names,experiments,index) def setExperiment(self, obj): experiment = self.experimentPulldown.getObject() if self.conditionPoint and (type(self.conditionPoint) != type(())): conditionSet = getExperimentConditionSet(experiment) if conditionSet is not self.conditionPoint.parent: if experiment not in self.expSeries.experiments: self.expSeries.addExperiment(experiment) unit = self.conditionPoint.unit if not unit: unit = CONDITION_UNITS_DICT[self.expSeries.conditionNames[0]] condition = self.conditionPoint.condition experiments = set(self.expSeries.experiments) for experiment0 in self.conditionPoint.parent.experiments: if experiment0 in experiments: experiments.remove(experiment0) self.expSeries.experiments = experiments value = self.conditionPoint.value error = self.conditionPoint.error self.conditionPoint.delete() self.conditionPoint = conditionSet.findFirstSampleCondition(condition=condition) if self.conditionPoint: self.conditionPoint.unit = unit self.updateAfter() else: self.conditionPoint = conditionSet.newSampleCondition(condition=condition,unit=unit, value=value,error=error) def updateDataDim(self, sampledDataDim): experiment = sampledDataDim.dataSource.experiment self.updateExperiments(experiment) def updateExperiments(self, experiment): experiments = self.getExperiments() names = [e.name for e in experiments] self.experimentPulldown.setup(names, experiments,0) if getExperimentSampledDim(experiment): self.updateAfter() elif self.expSeries: if self.expSeries.className == 'Experiment': if experiment is self.expSeries: self.updateConditionsAfter() elif experiment in self.expSeries.experiments: self.updateConditionsAfter() def updateConditionsAfter(self, sampleCondition=None): if self.waitingConditions: return if sampleCondition: experiments = sampleCondition.sampleConditionSet.experiments for experiment in experiments: if self.expSeries.className == 'Experiment': if experiment is self.expSeries: self.waitingConditions = True self.after_idle(self.updateConditions) break elif experiment in self.expSeries.experiments: self.waitingConditions = True self.after_idle(self.updateConditions) break else: self.waitingConditions = True self.after_idle(self.updateConditions) def updateConditions(self): self.updateButtons() objectList = [] textMatrix = [] colorMatrix = [] nCols = len(self.conditionPointsMatrix.headingList) defaultColors = [None] * nCols if self.expSeries: if self.expSeries.className == 'Experiment': dataDim = getExperimentSampledDim(self.expSeries) analysisDataDim = getAnalysisDataDim(dataDim) conditionVaried = dataDim.conditionVaried expName = self.expSeries.name unit = dataDim.unit pointValues = dataDim.pointValues pointErrors = dataDim.pointErrors refPlane = analysisDataDim.refSamplePlane for i in range(dataDim.numPoints): if i < len(pointErrors): error = pointErrors[i] else: error = None pointText = ':%3d' % (i+1) if i == refPlane: datum = ['* Ref Plane *', expName+pointText, None, None, None] colorMatrix.append(['#f08080'] * nCols) else: datum = [conditionVaried , expName+pointText, pointValues[i], error, unit] colorMatrix.append(defaultColors) textMatrix.append(datum) objectList.append((dataDim, i)) else: condDict = getNmrExpSeriesSampleConditions(self.expSeries) conditionNames = self.expSeries.conditionNames for conditionName in conditionNames: for sampleCondition in condDict.get(conditionName, []): datum = [sampleCondition.condition, ' '.join([e.name for e in sampleCondition.parent.experiments]), sampleCondition.value, sampleCondition.error, sampleCondition.unit] textMatrix.append(datum) objectList.append(sampleCondition) colorMatrix.append(defaultColors) self.conditionPointsMatrix.update(objectList=objectList, colorMatrix=colorMatrix, textMatrix=textMatrix) self.waitingConditions = 0 def updateAfter(self, object=None): if self.waiting: return else: self.waiting = True self.after_idle(self.update) def updateButtons(self): if self.expSeries is None: self.seriesButtons.buttons[1].disable() self.seriesButtons.buttons[2].disable() self.conditionPointsButtons.buttons[0].disable() self.conditionPointsButtons.buttons[1].disable() self.conditionPointsButtons.buttons[2].disable() elif self.expSeries.className == 'Experiment': self.seriesButtons.buttons[1].disable() self.seriesButtons.buttons[2].disable() self.conditionPointsButtons.buttons[0].disable() self.conditionPointsButtons.buttons[1].disable() self.conditionPointsButtons.buttons[2].enable() else: self.seriesButtons.buttons[1].enable() self.seriesButtons.buttons[2].enable() self.conditionPointsButtons.buttons[0].enable() self.conditionPointsButtons.buttons[2].disable() if self.conditionPoint is None: self.conditionPointsButtons.buttons[1].disable() else: self.conditionPointsButtons.buttons[1].enable() def update(self): self.updateButtons() objectList = [] textMatrix = [] for experiment in getSampledDimExperiments(self.nmrProject): getExperimentConditionSet(experiment) sampledDim = getExperimentSampledDim(experiment) datum = [None, experiment.name, 1, sampledDim.conditionVaried, sampledDim.numPoints] textMatrix.append(datum) objectList.append(experiment) for expSeries in self.nmrProject.sortedNmrExpSeries(): experiments = expSeries.experiments conditionSets = len([e.sampleConditionSet for e in experiments if e.sampleConditionSet]) datum = [expSeries.serial, expSeries.name or ' ', len(experiments), ','.join(expSeries.conditionNames), conditionSets] textMatrix.append(datum) objectList.append(expSeries) self.seriesMatrix.update(objectList=objectList, textMatrix=textMatrix) self.updateConditions() self.waiting = False def destroy(self): self.administerNotifiers(self.unregisterNotify) BasePopup.destroy(self)
class AddContourFilePopup(BasePopup): """ **Add Existing Contour Files to Project** The purpose of this dialog is to allow the user to add pre-existing contour files to the project. Contour files only depend on the spectrum data so the same contour files can be used across multiple projects, and that is the reason this dialog might be used. See also: `Spectrum Contour Files`_, `Creating Contour Files`_. .. _`Spectrum Contour Files`: EditContourFilesPopup.html .. _`Creating Contour Files`: CreateContourFilePopup.html """ def __init__(self, parent, *args, **kw): self.spectrum = None BasePopup.__init__(self, parent=parent, title='Add existing contour file', **kw) def body(self, master): self.geometry('600x130') master.grid_columnconfigure(1, weight=1) for n in range(5): master.grid_rowconfigure(n, weight=1) row = 0 label = Label(master, text='Spectrum: ') label.grid(row=row, column=0, sticky='e') tipText = 'The spectrum for which the contour file is being added' self.expt_spectrum = PulldownList(master, callback=self.updateContourDir, tipText=tipText) self.expt_spectrum.grid(row=row, column=1, sticky='w') row = row + 1 tipText = 'The location of the directory where contour files are stored on disk' label = Label(master, text='Contour dir: ') label.grid(row=row, column=0, sticky='e') self.dir_label = Label(master, text='', tipText=tipText) self.dir_label.grid(row=row, column=1, sticky='w') row = row + 1 label = Label( master, text= '(file will be copied into Contour dir if it is not already in there)' ) label.grid(row=row, column=1, sticky='w') row = row + 1 tipText = 'Browse for a file store contour data' button = Button(master, text='File name: ', command=self.selectFile, tipText=tipText) button.grid(row=row, column=0, sticky='e') tipText = 'Enter the name of the file to store contour data' self.file_entry = Entry(master, tipText=tipText) self.file_entry.grid(row=row, column=1, sticky='ew') row = row + 1 texts = ['Add File'] commands = [self.addFile] tipTexts = [ 'Use the selected contour file in the current project, copying it to the contour directory if required', ] self.buttons = UtilityButtonList(master, texts=texts, doClone=False, tipTexts=tipTexts, commands=commands, helpUrl=self.help_url) self.buttons.grid(row=row, column=0, columnspan=2, sticky='ew') self.curateNotifiers(self.registerNotify) self.updateSpectrum() def destroy(self): self.curateNotifiers(self.unregisterNotify) BasePopup.destroy(self) def curateNotifiers(self, notifyFunc): for clazz in ('Experiment', 'DataSource'): for func in ('__init__', 'delete', 'setName'): notifyFunc(self.updateNotifier, 'ccp.nmr.Nmr.%s' % clazz, func) def 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.updateContourDir(spectrum) def updateNotifier(self, *extra): self.updateSpectrum() def updateContourDir(self, spectrum): if spectrum is self.spectrum: return self.spectrum = spectrum if spectrum: path = spectrum.analysisSpectrum.contourDir.dataLocation else: path = '' self.dir_label.set(path) def selectFile(self): spectrum = self.spectrum if spectrum: directory = spectrum.analysisSpectrum.contourDir.dataLocation else: directory = os.getcwd() popup = FileSelectPopup(self, directory=directory) fileName = popup.getFile() popup.destroy() if fileName: self.file_entry.set(fileName) def addFile(self): spectrum = self.spectrum if not spectrum: return dataStore = spectrum.dataStore if not dataStore: showError('No dataStore', 'Spectrum does not have associated dataStore', parent=self) return if not isinstance(dataStore, BlockedBinaryMatrix): showError('No blockedBinaryMatrix', 'Spectrum dataStore is not a blockedBinaryMatrix', parent=self) return if not dataStore.blockSizes: showError('No blockSize', 'Spectrum dataStore does not have blockSize set', parent=self) return blockSize = list(dataStore.blockSizes) fileName = self.file_entry.get() if not fileName: showError('No filename', 'No filename given', parent=self) return fileName = normalisePath(fileName, makeAbsolute=True) contourDir = spectrum.analysisSpectrum.contourDir.dataLocation if fileName.startswith(contourDir): path = fileName[len(contourDir) + 1:] else: path = os.path.basename(fileName) if not os.path.exists(contourDir): os.makedirs(contourDir) print 'Copying %s to %s' % (fileName, contourDir) shutil.copy(fileName, contourDir) try: header = getStoredContourHeader(fileName) except Exception, e: showError('File error', str(e), parent=self) return if header['ndim'] != spectrum.numDim: showError( 'Number of dimensions', 'Number of dimensions in file (%d) does not match spectrum (%d)' % (header['ndim'], spectrum.numDim), parent=self) return dataDims = spectrum.sortedDataDims() npoints = [x.numPoints for x in dataDims] if header['npoints'] != npoints: showError( 'Number of points', 'Number of points in file (%s) does not match spectrum (%s)' % (header['npoints'], npoints), parent=self) return if header['blockSize'] != blockSize: showError('Block size', 'Block size in file (%s) does not match spectrum (%s)' % (header['blockSize'], blockSize), parent=self) return createStoredContour(spectrum, path, header['xdim'], header['ydim']) showInfo('Added file', 'Successfully added stored contour file', parent=self)