class ImageSaveDialog(ModelessDialog): name = "Save Image" buttons = ['Tips', 'Save As', Cancel] default = 'Save As' help = 'UsersGuide/print.html' provideStatus = True def fillInUI(self, master): self._ModelTrigger = None #self._SetupTrigger = None self._SizeTrigger = None self.raytrace = None # Image Size imageSetup = Tix.LabelFrame(master, label="Image Size") imageSetup.pack(fill=Tk.X, ipadx=2, ipady=2) subframe = imageSetup.frame #subframe = Tk.Frame(imageSetup.frame) #subframe.pack(fill=Tk.BOTH, expand=1) # add in conversion factor for pixels and graphics screen from chimera import tkgui win = tkgui.app.graphics res = tkgui.getScreenMMWidth() / win.winfo_screenwidth() convert['pixels'] = mm2pt(res) self.matchAspect = Tk.BooleanVar(master) self.matchAspect.set(1) self.usePrint = Tk.BooleanVar(master) self.usePrint.set(preferences.get(IMAGE_SETUP, USE_PRINT_UNITS)) import itertools row = itertools.count() self.showUsePrint = Tk.Checkbutton(subframe, indicatoron=1, variable=self.usePrint, highlightthickness=0, text=USE_PRINT_UNITS, command=self._updateUsePrint) self.showUsePrint.grid(columnspan=2, row=row.next(), sticky=Tk.W) w, h = chimera.viewer.windowSize self.units = ImageUnitsOption(subframe, row.next(), UNITS, 'pixels', self.updateImageUnits) self.iWidth = FloatOption(subframe, row.next(), 'Image width', w, self.updateImageWidth, min=1e-10) self.iHeight = FloatOption(subframe, row.next(), 'Image height', h, self.updateImageHeight, min=1e-10) matchAspect = Tk.Checkbutton(subframe, indicatoron=1, variable=self.matchAspect, highlightthickness=0, text="Maintain current aspect ratio", command=self.updateMatchAspect) matchAspect.grid(columnspan=2, row=row.next(), sticky=Tk.W) self.grow = Tk.Button(imageSetup.frame, text="Grow to Fit", command=self.Resize, state=Tk.DISABLED) fitrow = row.next() self.grow.grid(row=fitrow, column=0, padx=2, pady=2, sticky=Tk.NSEW) self.shrink = Tk.Button(imageSetup.frame, text="Shrink to Fit", command=lambda f=self.Resize: f(False), state=Tk.DISABLED) self.shrink.grid(row=fitrow, column=1, padx=2, pady=2, sticky=Tk.NSEW) #fetch = Tk.Button(imageSetup.frame, text="Get Pixels", # command=self.fetchWindowSize) #fetch.grid(row=row.next(), column=0, padx=2, pady=2, sticky=Tk.NSEW) #calc = Tk.Button(imageSetup.frame, text="Image Setup", # command=self.showImageSetupDialog) #calc.grid(row=row.next(), column=1, padx=2, pady=2, sticky=Tk.NSEW) self.printRes = FloatOption(subframe, row.next(), DPI, preferences.get(IMAGE_SETUP, DPI), self._updatePrint, min=1) # Image Information info = Tix.LabelFrame(master, label="Image Information") info.pack(fill=Tk.X) d = Tk.Label(info.frame, text="Description:") d.grid(columnspan=2, row=0, column=0, sticky=Tk.W, padx=2, pady=1) self.description = Tk.Entry(info.frame) info.frame.grid_columnconfigure(0, weight=1) info.frame.grid_columnconfigure(1, weight=1) self.description.grid(columnspan=2, row=1, column=0, sticky=Tk.EW, padx=2, pady=2) imageCredits = Tk.Button(info.frame, text="Image Credits", command=self.showImageCreditsDialog) imageCredits.grid(row=2, column=0, padx=2, pady=2) credit = Tk.Button(info.frame, text="Citing Chimera", command=lambda: help.display("credits.html")) credit.grid(row=2, column=1, padx=2, pady=2) # Image camera self.raytrace = BooleanOption(master, -1, 'Raytrace with POV-Ray', False, self._updateRaytrace) self.raytraceOptions = Tk.Button(master, text=POVRAY_SETUP, command=self.showPOVRayOptions) self.raytraceOptions.pack() self.raytraceOptions.pack_forget() self.supersample = SupersampleOption(master, -1, SUPERSAMPLE, preferences.get(IMAGE_SETUP, SUPERSAMPLE), self._updateSS) self.adjustFOV = AdjustFOVOption(master, -1, ADJUST_FOV, preferences.get(IMAGE_SETUP, ADJUST_FOV), self._updateAdjustFOV) self.printMode = _PrintModeOption(master, -1, 'Image camera mode', _PrintModeOption.SAME, self._updatePrintMode) self.lenticular = IntOption(master, -1, 'Number of lenticular images', chimera.Camera.lenticularImageCount(), self._updateLenticular, min=2, width=4) self.lenticular.forget() # switch to user's prefered units self.adjust = convert['pixels'] units = preferences.get(IMAGE_SETUP, UNITS) self._updateUsePrint() self.units.set(units) self.updateImageUnits() def map(self, event=None): self._ModelTrigger = chimera.triggers.addHandler('Model', self._computeMaxLineWidth, None) #self._SetupTrigger = chimera.triggers.addHandler(IMAGE_SETUP, # self._computeMaxLineWidth, None) self._SizeTrigger = chimera.triggers.addHandler( 'graphics window size', self._resetSize, None) self._computeMaxLineWidth() def unmap(self, event=None): if self._ModelTrigger: chimera.triggers.deleteHandler('Model', self._ModelTrigger) self._ModelTrigger = None #if self._SetupTrigger: # chimera.triggers.deleteHandler(IMAGE_SETUP, # self._SetupTrigger) # self._SetupTrigger = None def _computeMaxLineWidth(self, *args): if self.raytrace and self.raytrace.get(): # not relevant if raytracing self.status('', blankAfter=0) return # this should be called models are modified, when the # image size changes and when image setup parameters # change (DPI, supersampling) opengl_max = min( chimera.opengl_getFloat("max point size"), chimera.opengl_getFloat("max line width")) max_lw = max([m.lineWidth for m in chimera.openModels.list(all=True) if hasattr(m, 'lineWidth')]) # compute tile scaling factor like C++ code does width = self.iWidth.get() height = self.iHeight.get() horizPixels, vertPixels, supersample = \ imageArgs(self.units.get(), width, height) width, height = chimera.viewer.windowSize tileScale = float(horizPixels) / float(width) tmp = float(vertPixels) / float(height) if tmp > tileScale: tileScale = tmp tileScale *= supersample opengl_max /= tileScale if max_lw > opengl_max: color = 'red' else: color = 'black' self.status('effective maximum line width is %g' % opengl_max, color=color, blankAfter=0) #def Print(self): # self.Cancel() # image, options = self.getImage() # filename = tempfile.mktemp() # image.save(filename, **options) # cmd = preferences.get(PAGE_SETUP, PRINTER_CMD) # cmd = re.sub('%s', "'" + filename + "'", cmd) # os.system(cmd) # os.unlink(filename) def SaveAs(self): # now save the image self.Cancel() printMode = self.printMode.get() raytrace = self.raytrace.get() if printMode == _PrintModeOption.SAME: printMode = None if not chimera.nogui and raytrace and chimera.viewer.clipping: dialog = ClipWarning() if not dialog.run(chimera.tkgui.app): return saveImage(None, self.iWidth.get(), self.iHeight.get(), units=self.units.get(), master=self.uiMaster(), description=self.description.get().strip(), printMode=printMode, raytrace=raytrace) def Tips(self): import help help.display(self.help + "#tips") def Resize(self, larger=True): vw, vh = chimera.viewer.windowSize iw = self.iWidth.get() ih = self.iHeight.get() vaspect = vw / float(vh) iaspect = iw / float(ih) from chimera import tkgui top = tkgui.app.winfo_toplevel() if larger: if vaspect < iaspect: w = int(vw * iaspect / vaspect + 0.5) if w != vw: top.wm_geometry('') tkgui.app.graphics.config(width=w) elif vaspect > iaspect: h = int(vh * vaspect / iaspect + 0.5) if h != vh: top.wm_geometry('') tkgui.app.graphics.config(height=h) else: if vaspect < iaspect: h = int(vh * vaspect / iaspect + 0.5) if h != vh: top.wm_geometry('') tkgui.app.graphics.config(height=h) elif vaspect > iaspect: w = int(vw * iaspect / vaspect + 0.5) if w != vw: top.wm_geometry('') tkgui.app.graphics.config(width=w) def fetchWindowSize(self): w, h = chimera.viewer.windowSize self.units.set('pixels') self.updateImageUnits() self.iWidth.set(w) self.iHeight.set(h) def _updateRaytrace(self, option): raytrace = option.get() if raytrace: self.printMode.forget() self.lenticular.forget() self.supersample.forget() self.raytraceOptions.pack() else: self.raytraceOptions.pack_forget() self.printMode.manage() self.supersample.manage() self._updatePrintMode(self.printMode) self._computeMaxLineWidth() def _updatePrintMode(self, option): printMode = option.get() if printMode == 'lenticular': self.lenticular.manage() else: self.lenticular.forget() def _updateLenticular(self, option): count = option.get() chimera.Camera.setLenticularImageCount(count) def updateMatchAspect(self, *args): if self.matchAspect.get(): self.grow.config(state=Tk.DISABLED) self.shrink.config(state=Tk.DISABLED) self.Resize() else: self.grow.config(state=Tk.NORMAL) self.shrink.config(state=Tk.NORMAL) def updateImageUnits(self, *args): units = self.units.get() if units == 'pixels': self.printRes.disable() self.adjustFOV.disable() else: self.printRes.enable() self.adjustFOV.enable() if units != preferences.get(IMAGE_SETUP, UNITS): preferences.set(IMAGE_SETUP, UNITS, units) try: adjust = convert[units] except KeyError: adjust = -1 if adjust == self.adjust: return if self.adjust != -1 and adjust != -1: factor = self.adjust / adjust w = self.iWidth.get() * factor self.iWidth.set(w) h = self.iHeight.get() * factor self.iHeight.set(h) if adjust == -1: # entering pixel mode w, h = chimera.viewer.windowSize self.iWidth.set(w) self.iHeight.set(h) elif self.adjust == -1: pass # leaving pixel mode, sanity check #w, h = paper_types[preferences.get(PAGE_SETUP, PAPER_TYPE)] #if self.iWidth.get() > w: # self.iWidth.set(w) #if self.iHeight.get() > h: # self.iHeight.set(h) self.adjust = adjust self._computeMaxLineWidth() def updateImageWidth(self, option): # adjust height to compensate for new width if self.matchAspect.get(): iWidth = option.get() w, h = chimera.viewer.windowSize self.iHeight.set(iWidth * h / w) self._computeMaxLineWidth() def updateImageHeight(self, option): # adjust width to compensate for new height if self.matchAspect.get(): iHeight = option.get() w, h = chimera.viewer.windowSize self.iWidth.set(iHeight * w / h) self._computeMaxLineWidth() def _resetSize(self, triggerName, myData, sizes): width, height, mmwidth, mmheight = sizes units = self.units.get() if units == 'pixels': self.iWidth.set(width) self.iHeight.set(height) else: adjust = convert['millimeters'] / convert[units] self.iWidth.set(mmwidth * adjust) self.iHeight.set(mmheight * adjust) self._computeMaxLineWidth() def _updateUsePrint(self): usePrint = self.usePrint.get() preferences.set(IMAGE_SETUP, USE_PRINT_UNITS, usePrint) if not usePrint: values = ['pixels'] else: values = convert.keys() try: values.remove('pixels') except ValueError: pass values.sort() self.units.values = values self.units.remakeMenu() if usePrint: self.units.set('inches') else: self.units.set('pixels') self.updateImageUnits() if not usePrint: self.fetchWindowSize() def _updatePrint(self, option): res = option.get() preferences.set(IMAGE_SETUP, DPI, res) def _updateAdjustFOV(self, option): adjust = option.get() preferences.set(IMAGE_SETUP, ADJUST_FOV, adjust) def _updateSS(self, option): ss = option.get() preferences.set(IMAGE_SETUP, SUPERSAMPLE, ss) self._computeMaxLineWidth() #def showImageSetupDialog(self, *args): # import dialogs # d = dialogs.display("preferences") # d.setCategoryMenu(IMAGE_SETUP) def showImageCreditsDialog(self, *args): import dialogs d = dialogs.display("preferences") d.setCategoryMenu(IMAGE_CREDITS) def showPOVRayOptions(self, *args): import dialogs d = dialogs.display("preferences") d.setCategoryMenu(POVRAY_SETUP)
class Match2Align(ModelessDialog): title = "Create Alignment from Superposition" oneshot = True help = "ContributedSoftware/matchalign/matchalign.html" def fillInUI(self, parent): row = 0 parent.columnconfigure(0, weight=1) parent.rowconfigure(row, weight=1) self.chainList = MoleculeChainScrolledListBox(parent, selectioncommand=self._updateIterRef, listbox_selectmode='multiple') self.chainList.grid(row=row, column=0, sticky='nsew') row += 1 mols = {} for chain in self.chainList.get(): mol = chain.molecule if mol in mols: continue mols[mol] = chain self.chainList.setvalue(mols.values()) f = Tkinter.Frame(parent) f.grid(row=row, column=0, sticky='w') row += 1 self.distCutoff = FloatOption(f, 0, "Residue-residue distance cutoff (angstroms)", prefs[DIST_CUTOFF], None, balloon="""\ residues whose principal atoms are further apart than this distance will not be aligned in the generated sequence alignment""") self.distCutoff.min = 0.0 class MatchTypeOption(SymbolicEnumOption): values = ["any", "all"] labels = ["at least one other", "all others"] f = Tkinter.Frame(parent) f.grid(row=row, column=0, sticky='w') row += 1 self.matchType = MatchTypeOption(f, 0, "Residue aligned in column if within cutoff of", prefs[ANYALL], None, balloon="""\ whether a residue needs to match the distance cutoff to all other residues in its column, or just to one residue in the column""") class GapCharOption(SymbolicEnumOption): values = [".", "-", "~"] labels = [". (period)", "- (dash)", "~ (tilde)"] f = Tkinter.Frame(parent) f.grid(row=row, column=0, sticky='w') row += 1 self.gapChar = GapCharOption(f, 0, "Gap character", prefs[GAPCHAR], None, balloon="""\ character used to depict gaps in alignment""") self.circularVar = Tkinter.IntVar(parent) self.circularVar.set(prefs[CIRCULAR]) Tkinter.Checkbutton(parent, variable=self.circularVar, text= "Allow for circular permutation").grid(row=row, column=0, sticky='w') row += 1 self.iterateVar = Tkinter.IntVar(parent) self.iterateVar.set(prefs[ITERATE]) Tkinter.Checkbutton(parent, command=self._iterParamsDisplay, text="Iterate superposition/alignment...", variable=self.iterateVar).grid( row=row, column=0, columnspan=2, sticky='w') row += 1 self.iterParams = Pmw.Group(parent, hull_padx=2, tag_text="Iteration Parameters") self.iterParams.grid(row=row, column=0, columnspan=2) row += 1 inside = self.iterParams.interior() Tkinter.Label(inside, text="Iterate alignment:").grid( row=0, column=0, rowspan=2, sticky='e') self.iterConvergeVar = Tkinter.IntVar(parent) self.iterConvergeVar.set(prefs[ITER_CONVERGE]) f = Tkinter.Frame(inside) f.grid(row=0, column=1, sticky='w') Tkinter.Radiobutton(f, value=False, text="at most", variable=self.iterConvergeVar).grid(row=0, column=0) self.iterLimit = Pmw.EntryField(f, labelpos='e', label_text="times", validate={'min': 1, 'validator': 'numeric'}, value=str(prefs[ITER_AMOUNT]), entry_width=2, entry_justify="center") self.iterLimit.grid(row=0, column=1) Tkinter.Radiobutton(inside, text="until convergence", value=True, variable=self.iterConvergeVar).grid( row=1, column=1, sticky='w') inside.rowconfigure(2, minsize="0.1i") Tkinter.Label(inside, text="Superimpose full columns:" ).grid(row=3, rowspan=2, column=0, sticky='e') self.iterAllColsVar = Tkinter.IntVar(parent) self.iterAllColsVar.set(prefs[ITER_ALL_COLS]) Tkinter.Radiobutton(inside, text="across entire alignment", value=True, variable=self.iterAllColsVar).grid( row=3, column=1, sticky='w') f = Tkinter.Frame(inside) f.grid(row=4, column=1, sticky='w') Tkinter.Radiobutton(f, text="in stretches of at least", variable=self.iterAllColsVar, value=False).grid(row=0, column=0) self.stretchLen = Pmw.EntryField(f, labelpos='e', label_text="consecutive columns", validate={'min': 2, 'validator': 'numeric'}, value=str(prefs[ITER_CONSECUTIVE_COLS]), entry_width=1, entry_justify="center") self.stretchLen.grid(row=0, column=1) self.referenceMenu = Pmw.OptionMenu(inside, labelpos='w', items=Pmw.ScrolledListBox.get(self.chainList), label_text="Reference chain for matching:") self.referenceMenu.grid(row=5, column=0, columnspan=2) self._iterParamsDisplay() f = Tkinter.Frame(parent) f.grid(row=row, column=0, columnspan=2, sticky='ew') row += 1 from chimera import help b = Tkinter.Button(f, text="Save settings", pady=0, command=self._saveSettings) b.grid(row=0, column=0) help.register(b, balloon="Save current settings") b = Tkinter.Button(f, text="Reset to defaults", pady=0, command=self._restoreSettings) b.grid(row=0, column=1) help.register(b, balloon="Reset dialog to factory defaults") f.columnconfigure(0, weight=1) f.columnconfigure(1, weight=1) def Apply(self): chains = self.chainList.getvalue() if len(chains) < 2: replyobj.error("Must choose at least two chains\n") self.enter() return mols = {} for chain in chains: if chain.molecule in mols: replyobj.error( "Please choose only one chain per model\n") self.enter() return mols[chain.molecule] = 1 cutoff = float(self.distCutoff.get()) matchType = self.matchType.get() gapChar = self.gapChar.get() circular = self.circularVar.get() from align import match2align from Midas import match, rmsd seqs = match2align(chains, cutoff, matchType, gapChar, circular) cols = alignedCols(seqs) replyobj.info("%d fully populated columns\n" % (len(cols))) if self.iterateVar.get(): best = None iteration = 1 refChain = self.chainList.itemMap[ self.referenceMenu.getvalue()] if self.iterConvergeVar.get(): iterLimit = None else: self.iterLimit.invoke() iterLimit = int(self.iterLimit.getvalue()) if self.iterAllColsVar.get(): stretchLen = 1 else: self.stretchLen.invoke() stretchLen = int(self.stretchLen.getvalue()) while True: refSeq = [s for s in seqs if s.molecule == refChain.molecule][0] # cull columns based on stretch len criteria stretch = [] culled = [] for col in cols: if not stretch or stretch[-1]+1 == col: stretch.append(col) continue if len(stretch) >= stretchLen: culled.extend(stretch) stretch = [col] if len(stretch) >= stretchLen: culled.extend(stretch) if stretchLen > 1: replyobj.info("%d fully populated" " columns in at least %d" " column stretches\n" % ( len(culled), stretchLen)) if not culled: break # match refAtoms = columnAtoms(refSeq, culled) for seq in seqs: if seq.molecule == refSeq.molecule: continue seqAtoms = columnAtoms(seq, culled) replyobj.info("Matching %s onto %s\n" % (seq.name, refSeq.name)) match(seqAtoms, refAtoms) seqs = match2align(chains, cutoff, matchType, gapChar, circular, statusPrefix= "Iteration %d: " % iteration) cols = alignedCols(seqs) replyobj.info("Iteration %d: %d fully populated" " columns\n" % (iteration, len(cols))) if best == None or len(cols) > len(best): best = cols else: break if iterLimit and iteration >= iterLimit: break iteration += 1 if len(seqs) == 2: mav = showAlignment(seqs, "Match of %s and %s" % (seqs[0].name, seqs[1].name)) else: mav = showAlignment(seqs, "Match -> Align (%d models)" % len(seqs)) from MultAlignViewer.MAViewer import MATCHED_REGION_INFO name, fill, outline = MATCHED_REGION_INFO mav.newRegion(name="Fully populated columns", columns=cols, fill=fill, outline=outline) if cols: # show pairwise RMSD matrix in fully populated columns matchAtoms = {} for seq in seqs: matchAtoms[seq] = columnAtoms(seq, cols) replyobj.info("\nEvaluating superpositions across all %d fully" " populated columns in the final alignment:\n" % len(cols)) dsqSum = 0 for i, s1 in enumerate(seqs): for s2 in seqs[i+1:]: v = rmsd(matchAtoms[s1], matchAtoms[s2], log=False) dsqSum += v * v replyobj.info("RMSD of %s with %s: %.3f\n" % (s1.name, s2.name, v)) from math import sqrt replyobj.info("Overall RMSD: %.3f\n\n" % sqrt(2 * dsqSum / (len(seqs) * (len(seqs)-1)))) mav.status("RMSDs reported in Reply Log", color="purple") replyobj.status("RMSDs reported in Reply Log", color="purple") else: mav.status("No fully populated columns in alignment", color="blue") def _iterParamsDisplay(self): if self.iterateVar.get(): self.iterParams.grid() else: self.iterParams.grid_remove() def _restoreSettings(self): from prefs import defaults self.distCutoff.set(defaults[DIST_CUTOFF]) self.matchType.set(defaults[ANYALL]) self.gapChar.set(defaults[GAPCHAR]) self.circularVar.set(defaults[CIRCULAR]) self.iterateVar.set(defaults[ITERATE]) self._iterParamsDisplay() self.iterConvergeVar.set(defaults[ITER_CONVERGE]) self.iterLimit.setvalue(str(defaults[ITER_AMOUNT])) self.iterAllColsVar.set(defaults[ITER_ALL_COLS]) self.stretchLen.setvalue(str(defaults[ITER_CONSECUTIVE_COLS])) def _saveSettings(self): prefs[DIST_CUTOFF] = float(self.distCutoff.get()) prefs[ANYALL] = self.matchType.get() prefs[GAPCHAR] = self.gapChar.get() prefs[CIRCULAR] = self.circularVar.get() prefs[ITERATE] = self.iterateVar.get() prefs[ITER_CONVERGE] = self.iterConvergeVar.get() self.iterLimit.invoke() prefs[ITER_AMOUNT] = int(self.iterLimit.getvalue()) prefs[ITER_ALL_COLS] = self.iterAllColsVar.get() self.stretchLen.invoke() prefs[ITER_CONSECUTIVE_COLS] = int(self.stretchLen.getvalue()) def _updateIterRef(self, *args): self.referenceMenu.setitems([self.chainList.valueMap[chain] for chain in self.chainList.getvalue()])
class AnisoDialog(ModelessDialog): title = "Thermal Ellipsoids" name = "anisotropic ellipsoids" buttons = ("Show Ellipsoids", "Hide Ellipsoids", "Close") help = "ContributedSoftware/thermal/thermal.html" builtinPresets = [("Simple ellipsoid", {}), ("Principal axes", { "ellipsoid": False, "axisFactor": 1.0 }), ("Principal ellipses", { "ellipsoid": False, "ellipseFactor": 1.0 }), ("Ellipsoid and principal axes", { "axisFactor": 1.5 }), ("Octant lines", { "ellipseFactor": 1.01, "ellipseColor": (0, 0, 0, 1) }), ("Snow globe axes", { "ellipsoidColor": (1, 1, 1, 1), "transparency": 50.0, "axisFactor": 0.99 }), ("Snow globe ellipses", { "ellipsoidColor": (1, 1, 1, 1), "transparency": 50.0, "ellipseFactor": 0.99 })] def fillInUI(self, parent): import Pmw, Tkinter top = parent.winfo_toplevel() menubar = Tkinter.Menu(top, type="menubar", tearoff=False) top.config(menu=menubar) self.presetsMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="Presets", menu=self.presetsMenu) presets = self.builtinPresets + prefs[PRESETS] for label, kw in presets: self.presetsMenu.add_command( label=label, command=lambda kw=kw: self.preset(**kw)) self.presetsMenu.add_separator() self.presetsMenu.add_command(label="Preset from current settings...", command=self.startDefinePreset) self.presetsMenu.add_command(label="Delete user preset...", command=self.startDeletePreset) from chimera.tkgui import aquaMenuBar row = aquaMenuBar(menubar, parent, row=0, columnspan=2) parent.columnconfigure(0, weight=1) parent.columnconfigure(1, weight=1) self._modFromCalc = False self.scaling = Pmw.EntryField(parent, labelpos='w', value='1', label_text="Scale factor:", entry_width=5, validate={ 'validator': 'real', 'min': 0.0 }, modifiedcommand=self._scaleTypingCB, command=self.ShowEllipsoids) self.scaling.grid(row=row, column=0) self.smoothing = Pmw.EntryField(parent, labelpos='w', label_text="Smoothing level:", validate={ 'validator': 'numeric', 'min': 1, 'max': 100 }, value='3', entry_width=2, command=self.ShowEllipsoids) self.smoothing.grid(row=row, column=1) row += 1 self.calculator = Pmw.EntryField( parent, labelpos='w', label_text="Set scale factor for probability (%):", entry_width=4, command=self._prob2scaling) self.calculator.grid(row=row, column=0, columnspan=2) row += 1 from chimera.tkoptions import SymbolicEnumOption, \ FloatOption, RGBAOption self.showEllipsoidVar = Tkinter.IntVar(parent) self.showEllipsoidVar.set(True) ellipsoidGroup = Pmw.Group(parent, tag_pyclass=Tkinter.Checkbutton, tag_text="Depict ellipsoids", tag_variable=self.showEllipsoidVar) ellipsoidGroup.grid(row=row, column=0, columnspan=2, sticky="ew") row += 1 inside = ellipsoidGroup.interior() inside.columnconfigure(0, weight=1, uniform=1) inside.columnconfigure(1, weight=1, uniform=1) self.ellipsoidColorOpt = RGBAOption( inside, 0, 'Color', None, None, noneOkay=True, balloon='"No color" means use atom colors') class TransparencyOption(SymbolicEnumOption): values = [ None, 0.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0 ] labels = [ "same as color", "0%", "10%", "20%", "30%", "40%", "50%", "60%", "70%", "80%", "90%", "100%" ] self.transparencyOpt = TransparencyOption(inside, 1, "Transparency", None, None) self.showAxesVar = Tkinter.IntVar(parent) self.showAxesVar.set(False) axesGroup = Pmw.Group(parent, tag_pyclass=Tkinter.Checkbutton, tag_text="Depict principal axes", tag_variable=self.showAxesVar) axesGroup.grid(row=row, column=0, columnspan=2, sticky="ew") row += 1 inside = axesGroup.interior() inside.columnconfigure(0, weight=1, uniform=1) inside.columnconfigure(1, weight=1, uniform=1) self.axisColorOpt = RGBAOption( inside, 0, 'Color', None, None, noneOkay=True, balloon='"No color" means use atom colors') self.axisFactorOpt = FloatOption(inside, 1, "Length factor", 1.5, self.ShowEllipsoids, balloon="relative to ellipsoids", sticky="w") self.axisThicknessOpt = FloatOption(inside, 2, "Thickness", 0.01, self.ShowEllipsoids, sticky="w") self.showEllipsesVar = Tkinter.IntVar(parent) self.showEllipsesVar.set(False) ellipsesGroup = Pmw.Group(parent, tag_pyclass=Tkinter.Checkbutton, tag_text="Depict principal ellipses", tag_variable=self.showEllipsesVar) ellipsesGroup.grid(row=row, column=0, columnspan=2, sticky="ew") row += 1 inside = ellipsesGroup.interior() inside.columnconfigure(0, weight=1, uniform=1) inside.columnconfigure(1, weight=1, uniform=1) self.ellipseColorOpt = RGBAOption( inside, 0, 'Color', None, None, noneOkay=True, balloon='"No color" means use atom colors') self.ellipseFactorOpt = FloatOption(inside, 1, "Size factor", 1.0, self.ShowEllipsoids, balloon="relative to ellipsoids", sticky="w") self.ellipseThicknessOpt = FloatOption(inside, 2, "Thickness", 0.02, self.ShowEllipsoids, sticky="w") self.selRestrictVar = Tkinter.IntVar(parent) self.selRestrictVar.set(False) Tkinter.Checkbutton(parent, text="Restrict Show/Hide to current" " selection, if any", variable=self.selRestrictVar).grid(row=row, column=0, columnspan=2) row += 1 def ShowEllipsoids(self, *args): if self.showAxesVar.get(): axisFactor = self.axisFactorOpt.get() else: axisFactor = None if self.showEllipsesVar.get(): ellipseFactor = self.ellipseFactorOpt.get() else: ellipseFactor = None kw = { 'color': self.ellipsoidColorOpt.get(), 'smoothing': int(self.smoothing.getvalue()), 'scale': float(self.scaling.getvalue()), 'showEllipsoid': self.showEllipsoidVar.get(), 'transparency': self.transparencyOpt.get(), 'axisFactor': axisFactor, 'axisColor': self.axisColorOpt.get(), 'axisThickness': self.axisThicknessOpt.get(), 'ellipseFactor': ellipseFactor, 'ellipseColor': self.ellipseColorOpt.get(), 'ellipseThickness': self.ellipseThicknessOpt.get() } if self.selRestrictVar.get(): from chimera.selection import currentAtoms selAtoms = currentAtoms() if selAtoms: kw['targets'] = selAtoms from Aniso import aniso from Midas import MidasError try: aniso(**kw) except MidasError: from chimera import UserError raise UserError("No atoms chosen had anisotropic" " information") def HideEllipsoids(self): kw = {} if self.selRestrictVar.get(): from chimera.selection import currentAtoms selAtoms = currentAtoms() if selAtoms: kw['targets'] = selAtoms from Aniso import unaniso unaniso(**kw) def preset(self, ellipsoid=True, ellipsoidColor=None, transparency=None, axisFactor=None, axisColor=None, axisThickness=0.01, ellipseFactor=None, ellipseColor=None, ellipseThickness=0.02): self.showEllipsoidVar.set(ellipsoid) if ellipsoid: self.ellipsoidColorOpt.set(ellipsoidColor) self.transparencyOpt.set(transparency) if axisFactor: self.showAxesVar.set(True) self.axisFactorOpt.set(axisFactor) self.axisColorOpt.set(axisColor) self.axisThicknessOpt.set(axisThickness) else: self.showAxesVar.set(False) if ellipseFactor: self.showEllipsesVar.set(True) self.ellipseFactorOpt.set(ellipseFactor) self.ellipseColorOpt.set(ellipseColor) self.ellipseThicknessOpt.set(ellipseThickness) else: self.showEllipsesVar.set(False) self.ShowEllipsoids() def startDefinePreset(self): if not hasattr(self, "_presetNameDialog"): import Pmw self._presetNameDialog = Pmw.PromptDialog( self.uiMaster(), title="Preset Name", label_text="Preset name:", entryfield_labelpos='w', defaultbutton=0, buttons=('OK', 'Cancel'), command=self._definePresetCB) self._presetNameDialog.show() def startDeletePreset(self): if not prefs[PRESETS]: from chimera import UserError raise UserError("No user presets have been defined") if not hasattr(self, "_presetDeleteDialog"): import Pmw self._presetDeleteDialog = Pmw.SelectionDialog( self.uiMaster(), title="Delete Preset", label_text="Preset name:", scrolledlist_labelpos='n', defaultbutton=0, scrolledlist_items=[ps[0] for ps in prefs[PRESETS]], buttons=('OK', 'Cancel'), command=self._deletePresetCB) self._presetDeleteDialog.show() def _definePresetCB(self, button): self._presetNameDialog.withdraw() if button is None or button == 'Cancel': return name = self._presetNameDialog.get().strip() if not name: self._presetNameDialog.show() from chimera import UserError UserError("Must provide preset name") if name in [x[0] for x in self.builtinPresets]: self._presetNameDialog.show() from chimera import UserError UserError("Cannot redefine builtin preset") anisoKw = {} anisoKw['ellipsoid'] = self.showEllipsoidVar.get() if anisoKw['ellipsoid']: anisoKw['ellipsoidColor'] = self.ellipsoidColorOpt.get() anisoKw['transparency'] = self.transparencyOpt.get() if self.showAxesVar.get(): anisoKw['axisFactor'] = self.axisFactorOpt.get() anisoKw['axisColor'] = self.axisColorOpt.get() anisoKw['axisThickness'] = self.axisThicknessOpt.get() if self.showEllipsesVar.get(): anisoKw['ellipseFactor'] = self.ellipseFactorOpt.get() anisoKw['ellipseColor'] = self.ellipseColorOpt.get() anisoKw['ellipseThickness'] = self.ellipseThicknessOpt.get() newPreset = (name, anisoKw) customPresets = prefs[PRESETS][:] for info in customPresets: if info[0] == name: # changing _contents_ of kw dictionary means we don't # have to redefine the menu command action info[1].clear() info[1].update(anisoKw) break else: customPresets.append(newPreset) self.presetsMenu.insert_command( len(self.builtinPresets) + len(customPresets), label=name, command=lambda kw=anisoKw: self.preset(**kw)) prefs[PRESETS] = customPresets def _deletePresetCB(self, result): self._presetDeleteDialog.withdraw() if result is None or result == 'Cancel': self._presetDeleteDialog.destroy() delattr(self, '_presetDeleteDialog') return deletions = self._presetDeleteDialog.getvalue() remaining = [ps for ps in prefs[PRESETS] if ps[0] not in deletions] prefs[PRESETS] = remaining for name in deletions: self.presetsMenu.delete(name) self._presetDeleteDialog.destroy() delattr(self, '_presetDeleteDialog') def _prob2scaling(self): try: prob = float(self.calculator.getvalue()) / 100.0 if prob < 0.0 or prob >= 1.0: raise ValueError("bad prob") except ValueError: from chimera import UserError raise UserError("Probability must be >= 0 and < 100") from Aniso import prob2scale self._modFromCalc = True self.scaling.setvalue("%g" % prob2scale(prob)) self._modFromCalc = False def _scaleTypingCB(self): if not self._modFromCalc: self.calculator.setvalue("")