# --- UCSF Chimera Copyright --- # Copyright (c) 2000 Regents of the University of California. # All rights reserved. This software provided pursuant to a # license agreement containing restrictions on its disclosure, # duplication and use. This notice must be embedded in or # attached to all copies, including partial copies, of the # software or any revisions or derivations thereof. # --- UCSF Chimera Copyright --- # # $Id: __init__.py 40780 2015-10-09 17:38:54Z pett $ import chimera from chimera import selection, Molecule from chimera.baseDialog import ModelessDialog import Tkinter, Pmw from prefs import prefs, TARGET, \ ATTRS_ATOMS, ATTRS_RESIDUES, ATTRS_MOLECULES, ATTRS_SEGREGIONS, \ COLORS, COLOR_ATOMS, COLOR_RIBBONS, COLOR_SURFACES, SCALING,\ ATOM_STYLE, ATOM_RADII, NOVAL_RADIUS, WORM_RADII, NOVAL_WORM, \ WORM_STYLE, RESTRICT from CGLtk.optCascade import CascadeOptionMenu class AtomAttrs: menuName = 'atoms' prefName = ATTRS_ATOMS colorAtoms = True colorRibbons = False modelType = Molecule def __init__(self): self.screenedAttrs = { 'name': False, 'idatmIsExplicit': False, 'idatmType': str, 'drawMode': False, 'hide': False, 'label': False, 'numBonds': False, 'minimumLabelRadius': False, 'surfaceDisplay': False, 'vdw': False, 'selLevel': False, 'surfaceOpacity': False, 'serialNumber': False, 'defaultRadius': False, 'radius': False, 'coordIndex': False } self.screenedSubAttrs = set() self.ctype = None def selectedObjects(self): return selection.currentAtoms() def objectId(self, o, level): return o.oslIdent(level) def modelObjects(self, models): items = [] for model in models: items.extend(model.atoms) return items def objectsInModels(self, objects, models): mSet = set(models) items = [a for a in objects if a.molecule in mSet] return items def sortObjects(self, objList): from chimera.misc import oslCmp objList.sort(lambda a1, a2: oslCmp(a1.oslIdent(), a2.oslIdent())) def childObjects(self): return [] def childType(self): return self.ctype def colorItem(self, item, c, oc, **kw): if kw['colorAtoms']: if kw['opaqueAtoms']: item.color = oc else: item.color = c if kw['colorSurfaces']: item.surfaceColor = c item.surfaceOpacity = -1 item.vdwColor = c def setRadius(self, item, rad, style, restrict = None): if not restrict or item in restrict: item.radius = rad item.drawMode = style atomAttrs = AtomAttrs() class ResidueAttrs: menuName = 'residues' prefName = ATTRS_RESIDUES colorAtoms = True colorRibbons = True modelType = Molecule def __init__(self, childType = None): self.screenedAttrs = { 'name': False, 'ribbonDisplay': False, 'isSheet': False, 'ribbonDrawMode': False, 'selLevel': False, 'ringMode': False, 'numAtoms': False, 'ssId': False, 'fillMode': False, 'fillDisplay': False, 'uniprotIndex': False, 'heavyAtomCount': False, } self.screenedSubAttrs = set() self.ctype = childType def selectedObjects(self): return selection.currentResidues() def objectId(self, o, level): return o.oslIdent(level) def modelObjects(self, models): items = [] for model in models: items.extend(model.residues) return items def objectsInModels(self, objects, models): mset = set(models) items = [r for r in objects if r.molecule in mset] return items def sortObjects(self, objList): from chimera.misc import oslCmp objList.sort(lambda r1, r2: oslCmp(r1.oslIdent(), r2.oslIdent())) def childObjects(self, object): return object.oslChildren() def childType(self): return self.ctype def colorItem(self, item, c, oc, **kw): if kw['colorRibbons']: if kw['opaqueRibbons']: item.ribbonColor = oc else: item.ribbonColor = c if kw['colorAtoms'] or kw['colorSurfaces']: for a in item.atoms: if kw['colorAtoms']: if kw['opaqueAtoms']: a.color=oc else: a.color=c if kw['colorSurfaces']: a.surfaceColor=c a.surfaceOpacity = -1 a.vdwColor=c def setRadius(self, item, rad, style, restrict = None): for a in item.atoms: if not restrict or a in restrict: a.radius = rad a.drawMode = style def setWormRadius(self, item, rad, style, restrict = None): if not restrict or item in restrict: item.ribbonDrawMode = chimera.Residue.Ribbon_Round item.ribbonDisplay = True item.ribbonStyle = style residueAttrs = ResidueAttrs(atomAttrs) class MoleculeAttrs: menuName = 'molecules' prefName = ATTRS_MOLECULES colorAtoms = True colorRibbons = True modelType = Molecule def __init__(self, childType = None): self.screenedAttrs = { 'autochain': False, 'ballScale': False, 'lineType': False, 'lineWidth': False, 'id': False, 'subid': False, 'pointSize': False, 'ribbonHidesMainchain': False, 'selLevel': False, 'showStubBonds': False, 'stickScale': False, 'structureAssigned': False, 'surfaceOpacity': False, 'vdwDensity': False, 'clipThickness': False, 'aromaticLines': False, 'aromaticLineType': False, 'idatmValid': False, 'lowerCaseChains': False, 'useClipPlane': False, 'useClipThickness': False, 'aromaticMode': False, 'aromaticDisplay': False, 'residueLabelPos': False, 'numBonds': False, 'pdbVersion': False, 'ribbonSmoothing': False, 'ribbonStiffness': False, 'ribbonType': False, 'numAtoms': False, 'numResidues': False } self.screenedSubAttrs = set(['phi', 'psi', 'chi1', 'chi2', 'chi3', 'chi4', 'kdHydrophobicity']) self.ctype = childType def selectedObjects(self): return selection.currentMolecules(asDict=True) def objectId(self, o, level): return o.oslIdent(level) def modelObjects(self, models): return models def objectsInModels(self, objects, models): mset = set(models) items = [m for m in objects if m in mset] return items def sortObjects(self, objList): from chimera.misc import oslCmp objList.sort(lambda m1, m2: oslCmp(m1.oslIdent(), m2.oslIdent())) def childObjects(self, object): return object.residues def childType(self): return self.ctype def colorItem(self, item, c, oc, **kw): if kw['colorRibbons']: for r in item.residues: if kw['opaqueRibbons']: r.ribbonColor = oc else: r.ribbonColor = c if kw['colorAtoms'] or kw['colorSurfaces']: for a in item.atoms: if kw['colorAtoms']: if kw['opaqueAtoms']: a.color = oc else: a.color = c if kw['colorSurfaces']: a.surfaceColor = c a.surfaceOpacity = -1 a.vdwColor = c def setRadius(self, item, rad, style, restrict = None): for a in item.atoms: if not restrict or a in restrict: a.radius = rad a.drawMode = style def setWormRadius(self, item, rad, style, restrict = None): for r in item.residues: if not restrict or r in restrict: r.ribbonDrawMode = chimera.Residue.Ribbon_Round r.ribbonDisplay = True r.ribbonStyle = style moleculeAttrs = MoleculeAttrs(residueAttrs) from Segger.regions import Segmentation class SegRegionAttrs: menuName = 'segmentation regions' prefName = ATTRS_SEGREGIONS colorAtoms = False colorRibbons = False modelType = Segmentation def __init__(self): self.screenedAttrs = { 'mask_id':False, 'rid':False, 'smoothing_level':False, } self.screenedSubAttrs = set() self.ctype = None def selectedObjects(self): import Segger return Segger.SelectedRegions() def selectables(self, items): return [r.surface() for r in items if r.has_surface()] def objectId(self, r, level): id = ':%d' % r.rid if level == selection.SelGraph: id = r.segmentation.oslIdent(level) + id return id def modelObjects(self, models): items = [] for model in models: items.extend(model.regions) return items def objectsInModels(self, objects, models): mset = set(models) items = [a for a in objects if a.segmentation in mset] return items def sortObjects(self, objList): objList.sort(lambda r1, r2: cmp(r1.rid, r2.rid)) def childObjects(self): return [] def childType(self): return self.ctype def colorItem(self, item, c, oc, **kw): item.set_color(c.rgba()) segRegionAttrs = SegRegionAttrs() objectTypes = (atomAttrs, residueAttrs, moleculeAttrs, segRegionAttrs) intTypes = [int, long] floatTypes = [float] from numpy import dtype for bits in ["16", "32", "64", "128"]: for base in ["int", "uint"]: try: intTypes.append(dtype(base + bits)) except TypeError: pass try: floatTypes.append(dtype("float" + bits)) except TypeError: pass numericTypes = intTypes + floatTypes MODE_RENDER = "Render" MODE_SELECT = "Select" Modes = [MODE_RENDER, MODE_SELECT] # Map object name to menu entry name. attrsLabelMap = dict([(o, o.menuName) for o in objectTypes]) revAttrsLabelMap = dict([(o.menuName, o) for o in objectTypes]) attrsPrefMap = dict([(o.prefName, o) for o in objectTypes]) NO_ATTR = "choose attr" NO_RENDER_DATA = "Choose attribute to show histogram" NO_SELECT_DATA = "Choose attribute to show histogram/list" MENU_VALUES_LABEL = "Values" _LIST_NOVALUE = "(no value)" class ShowAttrDialog(ModelessDialog): title = "Render/Select by Attribute" buttons = ('OK', 'Apply', 'Close') provideStatus = True name = "render/select attrs" help = "ContributedSoftware/render/render.html" dewormLabel = "non-worm" def __init__(self): self.models = [] self.useableTypes = numericTypes self.additionalNumericTypes = () # boolean could be here instead self.additionalOtherTypes = (bool, basestring) self._attrVals = [None] * len(Modes) self._minVal = [None] * len(Modes) self._maxVal = [None] * len(Modes) ModelessDialog.__init__(self) chimera.triggers.addHandler(chimera.SCENE_TOOL_SAVE, self._sceneSave, None) chimera.triggers.addHandler(chimera.SCENE_TOOL_RESTORE, self._sceneRestore, None) def Apply(self): prefs[RESTRICT] = self.selRestrictVar.get() if self.modeNotebook.getcurselection() == MODE_SELECT: self._applySelect() else: fname = "_apply" + self.renderNotebook.getcurselection() getattr(self, fname)() def attrVals(self, mode=None): if mode is None: mode = self.modeNotebook.getcurselection() return self._attrVals[Modes.index(mode)] def configure(self, models=None, mode=None, attrsOf=None, attrName=None, fromModelListBox=False): curMenu = self._curAttrMenu() curAttr = curMenu.getvalue() curMode = self.modeNotebook.getcurselection() curTargetLabel = self.targetMenu.getvalue() curTarget = revAttrsLabelMap[curTargetLabel] if models != self.models and models is not None: newModels = None if fromModelListBox: oldSet = set(self.models) newSet = set(models) if newSet >= oldSet: newModels = newSet - oldSet else: self.modelListBox.setvalue(models, doCallback=False) self.models = models self._populateAttrsMenus(newModels=newModels) if not models or (curAttr is None and attrName is None): # _populateAttrsMenu has knocked the menu # button off of "choose attr"; arrange for # it to be restored... attrName = NO_ATTR if (mode == curMode or mode is None) \ and (attrsOf == curTargetLabel or attrsOf is None) \ and ([attrName] == curAttr or attrName is None): # no other changes if curAttr is not None: try: curMenu.invoke(curAttr) except ValueError: # attribute no longer present self._targetCB(curTargetLabel) if mode != curMode and mode is not None: self.modeNotebook.selectpage(mode) if attrsOf != curTargetLabel and attrsOf is not None: self.targetMenu.invoke(attrsOf) # prevent attr menu winding up on "choose attr" # even in attrName specified if self._needRefreshAttrs: self.refreshAttrs() # if an attribute name is specified, probably want an update... if attrName is not None: if attrName == NO_ATTR: self._targetCB(curTargetLabel) self.refreshMenu.entryconfigure( MENU_VALUES_LABEL, state="disabled") else: target = curTarget if attrsOf is None else revAttrsLabelMap[attrsOf] seen = self.seenAttrs[target] if attrName not in seen: self._populateAttrsMenus() seen = self.seenAttrs[target] if attrName in seen: if attrName in target.screenedAttrs: from chimera import replyobj replyobj.warning("Built-in attribute '%s' is" " automatically screened out of Render By" " Attribute menus" % attrName) else: self._curAttrMenu().invoke([attrName]) else: self._targetCB(curTargetLabel) self.refreshMenu.entryconfigure( MENU_VALUES_LABEL, state="normal") def fillInUI(self, parent): top = parent.winfo_toplevel() menubar = Tkinter.Menu(top, type="menubar", tearoff=False) top.config(menu=menubar) fileMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="File", menu=fileMenu) fileMenu.add_command(label="Save Attributes...", command=self._saveAttr) scalingMenu = Tkinter.Menu(menubar) menubar.add_cascade(label="Scaling", menu=scalingMenu) self.scalingVar = Tkinter.StringVar(parent) self.scalingVar.set(prefs[SCALING]) scalingMenu.add_radiobutton(label="Logarithmic", value="log", command=self._scalingCB, variable=self.scalingVar) scalingMenu.add_radiobutton(label="Linear", value="linear", command=self._scalingCB, variable=self.scalingVar) refreshMenu = Tkinter.Menu(menubar) self.refreshMenu = refreshMenu menubar.add_cascade(label="Refresh", menu=refreshMenu) refreshMenu.add_command(label="Menus", command=self.refreshAttrs) def _cmdCB(): menu = self._curAttrMenu() menu.invoke(menu.getvalue()) refreshMenu.add_command(label=MENU_VALUES_LABEL, command=_cmdCB) from chimera.tkgui import aquaMenuBar aquaMenuBar(menubar, parent, row = 0, columnspan = 2) self.targetMenu = Pmw.OptionMenu(parent, command=self._targetCB, items=[o.menuName for o in objectTypes], labelpos='w', label_text="Attributes of") self.targetMenu.grid(row=1, column=0) from chimera.widgets import ModelScrolledListBox self._needRefreshAttrs = True self.modelListBox = ModelScrolledListBox(parent, selectioncommand=lambda: self.configure( models=self.modelListBox.getvalue(), fromModelListBox=True), filtFunc = self.filterModels, listbox_selectmode="extended", labelpos="nw", label_text="Models") self.modelListBox.grid(row=1, column=1, sticky="nsew") self._renderOkApply = True self._attrOkApply = dict.fromkeys(Modes, False) self.modeNotebook = Pmw.NoteBook(parent, raisecommand=self._pageChangeCB) self.modeNotebook.add(MODE_RENDER) self.modeNotebook.add(MODE_SELECT) self.modeNotebook.grid(row=2, column=0, columnspan=2, sticky="nsew") parent.rowconfigure(2, weight=1) parent.columnconfigure(1, weight=1) renderFrame = self.modeNotebook.page(MODE_RENDER) selectFrame = self.modeNotebook.page(MODE_SELECT) self.renderAttrsMenu = {} self.selectAttrsMenu = {} for o in objectTypes: self.renderAttrsMenu[o] = CascadeOptionMenu(renderFrame, command=lambda mi, o=o: self._compileAttrVals(o, mi), labelpos='w', label_text="Attribute:") for o in objectTypes: self.selectAttrsMenu[o] = CascadeOptionMenu(selectFrame, command=lambda mi, o=o: self._compileAttrVals(o, mi), labelpos='w', label_text="Attribute:") from CGLtk.Histogram import MarkedHistogram histKw = { 'statusline': self.status, 'minlabel': True, 'maxlabel': True } if prefs[SCALING] == "log": histKw['scaling'] = 'logarithmic' else: histKw['scaling'] = 'linear' self.selFrames = [] self.selHistFrame = Tkinter.Frame(selectFrame) self.selFrames.append(self.selHistFrame) self.selHistFrame.grid(row=1, column=0, sticky="nsew") self.selHistFrame.rowconfigure(0, weight=1) self.selHistFrame.columnconfigure(0, weight=1) self.selectHistogram = MarkedHistogram(self.selHistFrame, colorwell=False, showmarkerhelp=False, **histKw) self.selectHistogram['datasource'] = NO_SELECT_DATA self.selectHistogram.grid(row=0, column=0, columnspan=2, sticky="nsew") histKw['selectcallback'] = self._selMarkerCB self.renderHistogram = MarkedHistogram(renderFrame, **histKw) self.renderHistogram.grid(row=1, column=0, sticky="nsew") self.renderHistogram['datasource'] = NO_RENDER_DATA selModeTexts = ["between markers (inclusive)", "outside markers", "no value"] Tkinter.Label(self.selHistFrame, text="Select:").grid( row=1, rowspan=len(selModeTexts), column=0, sticky='e') self.selModeVar = Tkinter.IntVar(selectFrame) gridKw = { 'column': 1, 'sticky': 'w' } for i, text in enumerate(selModeTexts): b = Tkinter.Radiobutton(self.selHistFrame, text=text, value=i, variable=self.selModeVar) gridKw['row'] = i+1 b.grid(**gridKw) self.selNoValueButtonInfo = (b, gridKw) self.selModeVar.set(0) self.selListFrame = Tkinter.Frame(selectFrame) self.selFrames.append(self.selListFrame) self.selListFrame.rowconfigure(0, weight=1) self.selListFrame.columnconfigure(0, weight=1) self.selectListBox = Pmw.ScrolledListBox(self.selListFrame, listbox_selectmode="multiple") self.selectListBox.grid(row=0, column=0, sticky="nsew") self.selBoolFrame = Tkinter.Frame(selectFrame) self.selFrames.append(self.selBoolFrame) self.selBoolVar = Tkinter.IntVar(selectFrame) self.selBoolVar.set(True) self.boolButtons = [] for label in ["False", "True", "No value"]: self.boolButtons.append(Tkinter.Radiobutton( self.selBoolFrame, text=label, value=len(self.boolButtons), variable=self.selBoolVar)) self.boolButtons[-1].grid(row=len(self.boolButtons)-1, column=0, sticky='w') renderFrame.rowconfigure(1, weight=1) renderFrame.columnconfigure(0, weight=1) selectFrame.rowconfigure(1, weight=1) selectFrame.columnconfigure(0, weight=1) self.renderColorMarkers = self.renderHistogram.addmarkers( activate=True, coordtype='relative') if len(prefs[COLORS]) == 1: self.renderColorMarkers.append( ((0.5, 0.0), prefs[COLORS][0])) else: self.renderColorMarkers.extend(map(lambda e: ((e[0] / float(len(prefs[COLORS]) - 1), 0.0), e[1]), enumerate(prefs[COLORS]))) self.renderRadiiMarkers = self.renderHistogram.addmarkers( newcolor='slate gray', activate=False, coordtype='relative') if len(prefs[ATOM_RADII]) == 1: self.renderRadiiMarkers.append(((0.5, 0.0), None)) else: self.renderRadiiMarkers.extend(map(lambda e: ((e[0] / float(len(prefs[ATOM_RADII]) - 1), 0.0), None), enumerate(prefs[ATOM_RADII]))) for i, rad in enumerate(prefs[ATOM_RADII]): self.renderRadiiMarkers[i].radius = rad self.renderWormsMarkers = self.renderHistogram.addmarkers( newcolor='pink', activate=False, coordtype='relative') if len(prefs[WORM_RADII]) == 1: self.renderWormsMarkers.append(((0.5, 0.0), None)) else: self.renderWormsMarkers.extend(map(lambda e: ((e[0] / float(len(prefs[WORM_RADII]) - 1), 0.0), None), enumerate(prefs[WORM_RADII]))) for i, rad in enumerate(prefs[WORM_RADII]): self.renderWormsMarkers[i].radius = rad self.selectMarkers = self.selectHistogram.addmarkers( coordtype='relative', minmarks=2, maxmarks=2) selMarkerColor = (0.0, 1.0, 0.0) self.selectMarkers.extend([((0.333, 0.0), selMarkerColor), ((0.667, 1.0), selMarkerColor)]) f = self.renderHistogram.component('widgetframe') self.radiusEntry = Pmw.EntryField(f, labelpos='w', validate={ 'validator': 'real', 'min': 0.001 }, entry_width=7, entry_state='disabled') self.entryColumn = int(f.grid_size()[1]) self.renderNotebook = Pmw.NoteBook(renderFrame) self.renderNotebook.add("Colors") self.renderNotebook.add("Radii") self.renderNotebook.add("Worms") self.renderNotebook.grid(row=2, column=0) self.selRestrictVar = Tkinter.IntVar(parent) self.selRestrictVar.set(prefs[RESTRICT]) srbut = Tkinter.Checkbutton(renderFrame, variable=self.selRestrictVar, text="Restrict OK/Apply to current selection, if any") srbut.grid(row=3, column=0) f = self.renderNotebook.page("Colors") self.colorAtomsVar = Tkinter.IntVar(f) self.colorAtomsVar.set(prefs[COLOR_ATOMS]) self.colorAtomsButton = Tkinter.Checkbutton(f, pady=0, text="Color atoms", variable=self.colorAtomsVar) self.colorAtomsButton.grid(row=0, column=0, sticky="w") self.opaqueAtomsVar = Tkinter.IntVar(f) self.opaqueAtomsVar.set(True) self.opaqueAtomsButton = Tkinter.Checkbutton(f, pady=0, text="Keep opaque", variable=self.opaqueAtomsVar) self.opaqueAtomsButton.grid(row=0, column=1) self.colorRibbonsVar = Tkinter.IntVar(f) self.colorRibbonsVar.set(prefs[COLOR_RIBBONS]) self.colorRibbonsButton = Tkinter.Checkbutton(f, pady=0, text="Color ribbons", variable=self.colorRibbonsVar) self.colorRibbonsButton.grid(row=1, column=0, sticky='w') self.opaqueRibbonsVar = Tkinter.IntVar(f) self.opaqueRibbonsVar.set(True) self.opaqueRibbonsButton = Tkinter.Checkbutton(f, pady=0, text="Keep opaque", variable=self.opaqueRibbonsVar) self.opaqueRibbonsButton.grid(row=1, column=1) self.colorSurfacesVar = Tkinter.IntVar(f) self.colorSurfacesVar.set(prefs[COLOR_SURFACES]) self.colorSurfacesButton = Tkinter.Checkbutton(f, pady=0, text="Color surfaces", variable=self.colorSurfacesVar) self.colorSurfacesButton.grid(row=2, column=0, sticky='w') from CGLtk.color.ColorWell import ColorWell self.noValueColorsFrame = Tkinter.Frame(f) self.noValueColorsFrame.gridKw = { "row": 3, "column": 0, "columnspan": 2 } self.noValueColorsFrame.grid(**self.noValueColorsFrame.gridKw) Tkinter.Label(self.noValueColorsFrame, text='No-value color:' ).grid(row=0, column=0, sticky='e') self.noValueWell = ColorWell(self.noValueColorsFrame, noneOkay=True) self.noValueWell.grid(row=0, column=1, sticky='w') from SurfaceColor import gui_palette_names palettes = gui_palette_names.keys() palettes.sort() self.paletteMenu = Pmw.OptionMenu(f, command=self.setPalette, items=palettes, initialitem="Blue-Red", labelpos='w', label_text="Palette:") self.paletteMenu.component('menubutton').config(state="disabled") self.paletteMenu.grid(row=4, column=0, columnspan=2) self.reverseColorsButton = Tkinter.Button(f, pady=0, text= "Reverse threshold colors", state="disabled", command=self.reverseColors) self.reverseColorsButton.grid(row=5, column=0, columnspan=2) self.colorKeyButton = Tkinter.Button(f, pady=0, text= "Create corresponding color key", state="disabled", command=self._colorKeyCB) self.colorKeyButton.grid(row=6, column=0, columnspan=2) f = self.renderNotebook.page("Radii") self.radiiWarning = Tkinter.Label(f, text= "Radii can only be set for\n" "atom, residue or molecule attributes.") self.radiiWarning.grid() # for later setnaturalsize self.radiiFrame = Tkinter.Frame(f) from chimera.tkoptions import SymbolicEnumOption, \ FloatOption, BooleanOption class AtomStyleOption(SymbolicEnumOption): labels = ["ball", "sphere"] values = [chimera.Atom.Ball, chimera.Atom.Sphere] self.atomStyle = AtomStyleOption(self.radiiFrame, 0, "Atom style", prefs[ATOM_STYLE], None, balloon= "How affected atoms will be depicted") self.doNoValueRadii = BooleanOption(self.radiiFrame, 1, "Affect no-value atoms", False, None, balloon= "Set radii for atoms not having this attribute\n" "or leave them as is") class RadiiOption(FloatOption): min = 0.001 self.noValueRadii = RadiiOption(self.radiiFrame, 2, "No-value radius", prefs[NOVAL_RADIUS], None, balloon= "Atoms without this attribute will be given this radius") f = self.renderNotebook.page("Worms") self.wormsWarning = Tkinter.Label(f, text= "Worms can only be used with\n" "residue or molecule attributes.") self.wormsWarning.grid() # for later setnaturalsize self.wormsFrame = Tkinter.Frame(f) from chimera.tkoptions import EnumOption class WormStyleOption(EnumOption): values = ["smooth", "segmented", self.dewormLabel] self.wormStyle = WormStyleOption(self.wormsFrame, 0, "Worm style", prefs[WORM_STYLE], lambda o: self.renderNotebook.setnaturalsize(), balloon= "How worm radius changes between residues:\n" " smooth: radius changes smoothly\n" " segmented: radius changes abruptly") self.doNoValueWorm = BooleanOption(self.wormsFrame, 1, "Affect no-value residues", True, None, balloon= "Change worm representation for residues not having\n" "this attribute or leave them as is") self.noValueWorm = RadiiOption(self.wormsFrame, 2, "No-value worm radius", prefs[NOVAL_WORM], None, balloon="Residues without this attribute will\n" "be given this radius") self.renderNotebook.configure(raisecommand=self._raisePageCB) self.renderNotebook.setnaturalsize() self.modeNotebook.setnaturalsize() self.targetMenu.invoke(attrsPrefMap[prefs[TARGET]].menuName) def filterModels(self, model): target = revAttrsLabelMap[self.targetMenu.getvalue()] return isinstance(model, target.modelType) def histogram(self, frame=False): if self.modeNotebook.getcurselection() == MODE_RENDER: return self.renderHistogram if frame: return self.selHistFrame return self.selectHistogram def maxVal(self, mode=None): if mode is None: mode = self.modeNotebook.getcurselection() return self._maxVal[Modes.index(mode)] def minVal(self, mode=None): if mode is None: mode = self.modeNotebook.getcurselection() return self._minVal[Modes.index(mode)] def refreshAttrs(self): if not self.uiMaster().winfo_ismapped(): # model-list widget is sleeping, delay update... self._needRefreshAttrs = True return self._needRefreshAttrs = False curMenu = self._curAttrMenu() curAttr = curMenu.getvalue() self._populateAttrsMenus() try: curMenu.index(curAttr) except ValueError: # attribute no longer present self._targetCB(self.targetMenu.getvalue()) def reverseColors(self): if len(self.renderColorMarkers) < 2: return self.renderColorMarkers.sort() rgbas = [cm['rgba'] for cm in self.renderColorMarkers] rgbas.reverse() for cm, rgba in zip(self.renderColorMarkers, rgbas): cm['rgba'] = rgba def setPalette(self, paletteName): from SurfaceColor import gui_palette_names, standard_color_palettes rgbas = standard_color_palettes[gui_palette_names[paletteName]] self.renderColorMarkers.sort() for i, cm in enumerate(self.renderColorMarkers): place = i * (len(rgbas)-1.0) / (len(self.renderColorMarkers)-1.0) leftIndex = int(place) if leftIndex == place or leftIndex == len(rgbas) - 1: rgba = rgbas[leftIndex] else: f = place - leftIndex rgba = tuple([l * (1-f) + r * f for l,r in zip(rgbas[leftIndex], rgbas[leftIndex+1])]) cm['rgba'] = rgba def _applyColors(self): colorAtoms = self.colorAtomsVar.get() opaqueAtoms = self.opaqueAtomsVar.get() colorRibbons = self.colorRibbonsVar.get() opaqueRibbons = self.opaqueRibbonsVar.get() colorSurfaces = self.colorSurfacesVar.get() ckw = {'colorAtoms': colorAtoms, 'opaqueAtoms': opaqueAtoms, 'colorRibbons': colorRibbons, 'opaqueRibbons': opaqueRibbons, 'colorSurfaces': colorSurfaces} if colorSurfaces: from chimera.actions import changeSurfsFromCustom changeSurfsFromCustom(self.models) target = revAttrsLabelMap[self.targetMenu.getvalue()] if (not (colorAtoms and target.colorAtoms) and not colorSurfaces and not (colorRibbons and target.colorRibbons)): raise chimera.UserError( "Nothing chosen to receive attribute coloring!") prefs[COLOR_ATOMS] = colorAtoms prefs[COLOR_RIBBONS] = colorRibbons prefs[COLOR_SURFACES] = colorSurfaces self.status("Coloring atoms/ribbons", blankAfter=0) def makeColors(rgba): c = chimera.MaterialColor(*rgba) if rgba[-1] < 1.0: orgba = list(rgba[:3]) + [1.0] oc = chimera.MaterialColor(*orgba) else: oc = c colors[val] = (c, oc) return c, oc colors = {} restrict = prefs[RESTRICT] if restrict: filterItems = target.selectedObjects() if not filterItems: restrict = False from operator import add attrMenu = self.renderAttrsMenu[target] if restrict: items = target.objectsInModels(filterItems, self.models) else: items = target.modelObjects(self.models) attrName = attrMenu.getvalue() if len(attrName) == 1: doSubitems = False else: doSubitems = True attrName = attrName[-1] self.renderColorMarkers['coordtype'] = 'absolute' noValRgba = self.noValueWell.rgba for item in items: if doSubitems: # an average/sum vals = [] for sub in target.childObjects(item): try: val = getattr(sub, attrName) except AttributeError: continue if val is not None: vals.append(val) if vals: val = reduce(add, vals) if not summableAttrName(attrName): val /= float(len(vals)) else: val = None else: try: val = getattr(item, attrName) except AttributeError: val = None if val is None: if noValRgba is not None: if None in colors: c, oc = colors[None] else: c, oc = makeColors(noValRgba) target.colorItem(item, c, oc, **ckw) continue if len(self.renderColorMarkers) == 0: continue for i, marker in enumerate(self.renderColorMarkers): if val <= self._markerVal(marker): break else: i = len(self.renderColorMarkers) if i == 0: val = self._markerVal( self.renderColorMarkers[0]) i = 1 elif i == len(self.renderColorMarkers): val = self._markerVal( self.renderColorMarkers[-1]) i -= 1; if val in colors: target.colorItem(item, *colors[val], **ckw) continue if len(self.renderColorMarkers) > 1: left, right = map(lambda m: (self._markerVal(m), m['rgba']), self.renderColorMarkers[i-1:i+1]) lval, lrgba = left rval, rrgba = right if rval == lval: pos = 0.5 else: pos = (val - lval) / float(rval - lval) rgba = map(lambda l, r: l*(1 - pos) + r*pos, lrgba, rrgba) else: rgba = self.renderColorMarkers[0]['rgba'] c, oc = makeColors(rgba) target.colorItem(item, c, oc, **ckw) self.renderColorMarkers['coordtype'] = 'relative' self.status("Done setting colors") def _applyRadii(self): markers, marker = self.renderHistogram.currentmarkerinfo() if marker is not None: self._setRadius(marker) noValRadius = self.noValueRadii.get() doNoVal = self.doNoValueRadii.get() prefs[ATOM_RADII] = map(lambda m: m.radius, self.renderRadiiMarkers[:]) prefs[ATOM_STYLE] = self.atomStyle.get() target = revAttrsLabelMap[self.targetMenu.getvalue()] self.status("Setting atomic radii", blankAfter=0) restrict = prefs[RESTRICT] if restrict: curSel = selection.currentAtoms(asDict=True) if not curSel: restrict = False if restrict: restrict = curSel from operator import add style = prefs[ATOM_STYLE] attrMenu = self.renderAttrsMenu[target] if restrict: items = target.objectsInModels(target.selectedObjects(), self.models) else: items = target.modelObjects(self.models) attrName = attrMenu.getvalue() if len(attrName) == 1: doSubitems = False else: doSubitems = True attrName = attrName[-1] radMarkers = self.renderRadiiMarkers radMarkers['coordtype'] = 'absolute' for item in items: if doSubitems: # an average/sum vals = [] for sub in target.childObjects(item): try: val = getattr(sub, attrName) except AttributeError: continue if val is not None: vals.append(val) if vals: val = reduce(add, vals) if not summableAttrName(attrName): val /= float(len(vals)) else: val = None else: try: val = getattr(item, attrName) except AttributeError: val = None if val is None: if doNoVal: target.setRadius(item, noValRadius, style, restrict) continue if len(radMarkers) == 0: continue for i, marker in enumerate(radMarkers): if val <= self._markerVal(marker): break else: i = len(radMarkers) if i == 0: rad = radMarkers[0].radius elif i == len(radMarkers): rad = radMarkers[-1].radius elif len(radMarkers) > 1: left, right = map(self._markerVal, radMarkers[i-1:i+1]) if right == left: pos = 0.5 else: pos = (val - left) / float(right - left) rad = radMarkers[i-1].radius * (1 - pos) + radMarkers[i].radius * pos else: rad = radMarkers[0].radius target.setRadius(item, rad, style, restrict) radMarkers['coordtype'] = 'relative' self.status("Done setting radii") def _applyWorms(self): markers, marker = self.renderHistogram.currentmarkerinfo() if marker is not None: self._setRadius(marker) noValRadius = self.noValueWorm.get() doNoVal = self.doNoValueWorm.get() wormStyleName = self.wormStyle.get() prefs[NOVAL_WORM] = noValRadius if wormStyleName == self.dewormLabel: # revert the style menu to the worm style self.wormStyle.set(prefs[WORM_STYLE]) else: prefs[WORM_STYLE] = wormStyleName prefs[ATOM_RADII] = map(lambda m: m.radius, self.renderWormsMarkers[:]) target = revAttrsLabelMap[self.targetMenu.getvalue()] self.status("Setting worm radii", blankAfter=0) restrict = prefs[RESTRICT] if restrict: curSel = selection.currentResidues(asDict=True) if not curSel: restrict = False if restrict: restrict = curSel styles = {} def style(rad): if wormStyleName == "smooth": # can't cache -- each different return chimera.RibbonStyleWorm([rad]) elif wormStyleName == self.dewormLabel: return None if rad not in styles: styles[rad] = chimera.RibbonStyleFixed([rad, rad]) return styles[rad] from operator import add if not hasattr(target, 'setWormRadius'): raise AssertionError, "Cannot worms with %s target" % target.menuName attrMenu = self.renderAttrsMenu[target] items = target.modelObjects(self.models) attrName = attrMenu.getvalue() if len(attrName) == 1: doSubitems = False else: doSubitems = True attrName = attrName[-1] radMarkers = self.renderWormsMarkers radMarkers['coordtype'] = 'absolute' for item in items: if doSubitems: # an average/sum vals = [] for sub in target.childObjects(item): try: val = getattr(sub, attrName) except AttributeError: continue if val is not None: vals.append(val) if vals: val = reduce(add, vals) if not summableAttrName(attrName): val /= float(len(vals)) else: val = None else: try: val = getattr(item, attrName) except AttributeError: val = None if val is None: if doNoVal: target.setWormRadius(item, noValRadius, style(noValRadius), restrict) continue if len(radMarkers) == 0: continue for i, marker in enumerate(radMarkers): if val <= self._markerVal(marker): break else: i = len(radMarkers) if i == 0: rad = radMarkers[0].radius elif i == len(radMarkers): rad = radMarkers[-1].radius elif len(radMarkers) > 1: left, right = map(self._markerVal, radMarkers[i-1:i+1]) if right == left: pos = 0.5 else: pos = (val - left) / float(right - left) rad = radMarkers[i-1].radius * (1 - pos) + radMarkers[i].radius * pos else: rad = radMarkers[0].radius target.setWormRadius(item, rad, style(rad), restrict) radMarkers['coordtype'] = 'relative' self.status("Done setting radii") def _applySelect(self): self.status("Selecting atoms/residues", blankAfter=0) from operator import add target = revAttrsLabelMap[self.targetMenu.getvalue()] attrMenu = self.selectAttrsMenu[target] items = target.modelObjects(self.models) attrName = attrMenu.getvalue() if len(attrName) == 1: doSubitems = False else: doSubitems = True attrName = attrName[-1] usingHist = False if self.selHistFrame.winfo_manager(): usingHist = True selMode = self.selModeVar.get() self.selectMarkers['coordtype'] = 'absolute' if selMode < 2: m1, m2 = map(lambda m: self._markerVal(m), self.selectMarkers) if selMode == 0: selFunc = lambda v: v >= m1 and v <= m2 else: selFunc = lambda v: v < m1 or v > m2 else: selFunc = lambda v: False elif self.selListFrame.winfo_manager(): selMode = 0 # i.e. not selecting None selValues = {} for v in self.selectListBox.getvalue(): if v == _LIST_NOVALUE: selMode = 2 # selecting None continue selValues[v] = 1 selFunc = lambda v: v in selValues else: # since it just so happens that the only relevant # value for selMode in the later code is 2... selMode = self.selBoolVar.get() selFunc = lambda v: v == selMode sels = [] for item in items: if doSubitems: # an average/sum vals = [] for sub in target.childObjects(item): try: val = getattr(sub, attrName) except AttributeError: continue if val is not None: vals.append(val) if vals: val = reduce(add, vals) if not summableAttrName(attrName): val /= float(len(vals)) else: val = None else: try: val = getattr(item, attrName) except AttributeError: val = None if val is None: if selMode == 2: sels.append(item) continue if selFunc(val): sels.append(item) if hasattr(target, 'selectables'): sels = target.selectables(sels) from chimera.selection import ItemizedSelection sel = ItemizedSelection() sel.add(sels) sel.addImplied() from chimera.tkgui import selectionOperation selectionOperation(sel) if usingHist: self.selectMarkers['coordtype'] = 'relative' self.status("Done selecting atoms/residues") def _colorKeyCB(self, *args): if len(self.renderColorMarkers) < 2: raise chimera.UserError("Need at least two color bars" " in histogram to create key") prevCoordType = self.renderColorMarkers['coordtype'] self.renderColorMarkers['coordtype'] = 'absolute' from Ilabel.gui import IlabelDialog from chimera import dialogs d = dialogs.display(IlabelDialog.name) d.keyConfigure([(m['rgba'], "%g" % self._markerVal(m)) for m in self.renderColorMarkers]) self.renderColorMarkers['coordtype'] = prevCoordType def _compileAttrVals(self, target, menuItem): self.status("Surveying attribute %s" % " ".join(menuItem), blankAfter=0) mode = self.modeNotebook.getcurselection() attrVals = [] surveyed = target.modelObjects(self.models) attrName = menuItem[-1] hasNone = False if len(menuItem) == 1: for t in surveyed: try: val = getattr(t, attrName) except AttributeError: hasNone = True continue if val is None or (isinstance(val, basestring) and not val): hasNone = True continue attrVals.append(val) else: # average/sum of atoms/residues from operator import add for t in surveyed: vals = [] for sub in target.childObjects(t): try: val = getattr(sub, attrName) except AttributeError: continue if val is None \ or (isinstance(val, basestring) and not val): continue vals.append(val) if not vals: hasNone = True continue val = reduce(add, vals) if not summableAttrName(attrName): val /= float(len(vals)) attrVals.append(val) self.status("Done surveying") self._attrOkApply[mode] = True self._setAttrVals(attrVals) if mode == MODE_SELECT: for frame in self.selFrames: frame.grid_forget() if attrVals and isinstance(attrVals[0], basestring): frame = self.selListFrame uniqueVals = {} for av in attrVals: uniqueVals[av] = 1 listItems = uniqueVals.keys() listItems.sort(lambda a, b: cmp(a.lower(), b.lower())) if hasNone: listItems.append(_LIST_NOVALUE) self.selectListBox.setlist(listItems) elif attrVals and isinstance(attrVals[0], bool): frame = self.selBoolFrame self.boolButtons[-1].grid_forget() if hasNone: self.boolButtons[-1].grid( row=len(self.boolButtons)-1, column=0, sticky='w') elif self.selBoolVar.get() == 2: self.selBoolVar.set(True) else: frame = self.selHistFrame if not attrVals: self.histogram()['datasource'] = \ "No attribute '%s' in any %s" % ( attrName, target.menuName) self._attrOkApply[mode] = False elif self.minVal() == self.maxVal(): self.histogram()['datasource'] = \ "attribute has only one value: %s" \ % str(self.minVal()) self._attrOkApply[mode] = False else: self.histogram()['datasource'] = (self.minVal(), self.maxVal(), lambda numBins, mode=mode: self._makeBins(numBins, mode=mode)) if hasNone: but, gridKw = self.selNoValueButtonInfo but.grid(**gridKw) else: self.selNoValueButtonInfo[0].grid_forget() if hasNone: self.noValueColorsFrame.grid( **self.noValueColorsFrame.gridKw) self.doNoValueRadii.gridManage() self.noValueRadii.gridManage() self.doNoValueWorm.gridManage() self.noValueWorm.gridManage() else: self.noValueColorsFrame.grid_forget() self.doNoValueRadii.gridUnmanage() self.noValueRadii.gridUnmanage() self.doNoValueWorm.gridUnmanage() self.noValueWorm.gridUnmanage() self.renderNotebook.setnaturalsize() if mode == MODE_RENDER: state = 'normal' if self._attrOkApply[mode] and self._renderOkApply else 'disabled' self.colorKeyButton.configure(state=state) self.reverseColorsButton.configure(state=state) self.paletteMenu.component('menubutton').config(state=state) else: state = 'normal' if self._attrOkApply[mode] else 'disabled' frame.grid(row=1, column=0, sticky="nsew") self.buttonWidgets['OK'].configure(state=state) self.buttonWidgets['Apply'].configure(state=state) if state == 'normal': if mode == MODE_RENDER or frame == self.selHistFrame: if mode == MODE_SELECT: addText = "" else: renderPage = self.renderNotebook.getcurselection() if renderPage == "Colors": addText = " or color" else: addText = " or radius" # report after histogram computation self.colorKeyButton.after(1000, lambda s1="Histogram bars can be moved with the mouse", s2="Click on histogram bar to set value" + addText: self.status(s1, followWith=s2, blankAfter=6)) self.refreshMenu.entryconfigure(MENU_VALUES_LABEL, state="normal") def _composeAttrMenus(self): sortFunc = lambda a, b: cmp(a.lower(), b.lower()) for o in objectTypes: self.useableAttrs[o].sort(sortFunc) for o in objectTypes: self.renderAttrsMenu[o].setitems(_menuItems( self.useableAttrs[o], self.useableAttrs.get(o.childType(),[]), o.screenedSubAttrs)) # do this in a little bit of a weird order so that the 'average' # submenu only contains numeric quantities aggAveAttrs = {None:[]} for o in objectTypes: aggAveAttrs[o] = self.useableAttrs[o] \ + self.additionalNumericAttrs[o] aggAveAttrs[o].sort(sortFunc) for o in objectTypes: aggAttrs = aggAveAttrs[o] + self.additionalOtherAttrs[o] aggAttrs.sort(sortFunc) self.selectAttrsMenu[o].setitems(_menuItems( aggAttrs, aggAveAttrs[o.childType()], o.screenedSubAttrs)) def _curAttrMenu(self): target = revAttrsLabelMap[self.targetMenu.getvalue()] if self.modeNotebook.getcurselection() == MODE_RENDER: return self.renderAttrsMenu[target] else: return self.selectAttrsMenu[target] def _makeBins(self, numBins, mode): self.status("Computing histogram bins", blankAfter=0) minVal, maxVal = self.minVal(mode), self.maxVal(mode) if isinstance(minVal, int) and isinstance(maxVal, int) \ and maxVal - minVal + 1 <= numBins / 3.0: # enough room to show bars instead of lines numBins = maxVal - minVal + 1 bins = [0] * numBins vrange = maxVal - minVal binSize = vrange / float(numBins - 1) leftEdge = minVal - 0.5 * binSize for val in self.attrVals(mode): bin = int((val - leftEdge) / binSize) bins[bin] += 1 self.status("Done computing histogram bins") return bins def _markerVal(self, marker): rawVal = marker['xy'][0] if isinstance(self.minVal(), int): return int(rawVal + 0.5) return rawVal def _pageChangeCB(self, pageName): if pageName == MODE_RENDER: state = 'normal' if self._attrOkApply[pageName] and self._renderOkApply else 'disabled' else: state = 'normal' if self._attrOkApply[pageName] else 'disabled' self.buttonWidgets['OK'].configure(state=state) self.buttonWidgets['Apply'].configure(state=state) def _populateAttrsMenus(self, newModels=None): self.status("Compiling attribute lists...", blankAfter=0) if newModels is None or not hasattr(self, 'seenAttrs'): self.seenAttrs = {} self.useableAttrs = {} self.additionalNumericAttrs = {} self.additionalOtherAttrs = {} for o in objectTypes: self.seenAttrs[o] = o.screenedAttrs.copy() self.useableAttrs[o] = [] self.additionalNumericAttrs[o] = [] self.additionalOtherAttrs[o] = [] if newModels is None: scanModels = self.models else: scanModels = newModels for o in objectTypes: self.status("Compiling attribute lists for %s..." % o.menuName, blankAfter=0) models = [m for m in scanModels if isinstance(m, o.modelType)] for item in o.modelObjects(models): self._reapAttrs(item, self.seenAttrs[o], self.useableAttrs[o], self.additionalNumericAttrs[o], self.additionalOtherAttrs[o]) self.status("Updating attribute menus...", blankAfter=0) self._composeAttrMenus() self.status("") def _raisePageCB(self, page): entryFrame = self.renderHistogram.component('widgetframe') if page == "Colors": markers = self.renderColorMarkers self.renderHistogram.configure(colorwell=True) self.radiusEntry.grid_forget() entryFrame.columnconfigure(self.entryColumn, weight=0) else: if page == "Radii": markers = self.renderRadiiMarkers self.radiusEntry.component('label').configure( text='Atom radius') else: markers = self.renderWormsMarkers self.radiusEntry.component('label').configure( text='Worm radius') self.renderHistogram.configure(colorwell=False) self.radiusEntry.grid(row=1, column=self.entryColumn) entryFrame.columnconfigure(self.entryColumn, weight=2) self.renderHistogram.activate(markers) self._renderGUI() def _reapAttrs(self, instance, seenAttrs, useableAttrs, additionalNumericAttrs, additionalOtherAttrs): for attrName in dir(instance): attrType = seenAttrs.get(attrName, None) if isinstance(attrType, bool): continue elif attrType is None: if attrName[0] == '_' \ or attrName[0].isupper(): # e.g. 'Ball' seenAttrs[attrName] = False continue attr = getattr(instance, attrName) if attr is None or (isinstance(attr, basestring) and not attr): # defer judgment until # we see a real value continue attrType = type(attr) else: attr = attrType() seenAttrs[attrName] = True if attrType in self.useableTypes: useableAttrs.append(attrName) elif attrType in self.additionalNumericTypes: additionalNumericAttrs.append(attrName) elif isinstance(attr, self.additionalOtherTypes): additionalOtherAttrs.append(attrName) def _sceneRestore(self, trigName, myData, scene): data = scene.tool_settings.get(self.name, None) if data is None: self.Close() return if data['shown']: self.enter() else: self.Close() self.targetMenu.invoke(data['target']) from Animate.Tools import idLookup self.modelListBox.setvalue([idLookup(sid) for sid in data['models']]) self.modeNotebook.selectpage(data['mode']) try: self._curAttrMenu().invoke(data['attribute']) except ValueError: # attribute not in menu self.Close() return self.histogram().sceneRestore(data['hist data']) self.renderNotebook.selectpage(data['action tab']) for dataName, widget in [ ('sel mode', self.selModeVar), ('sel bool', self.selBoolVar), ('color atoms', self.colorAtomsVar), ('opaque atoms', self.opaqueAtomsVar), ('color ribbons', self.colorRibbonsVar), ('opaque ribbons', self.opaqueRibbonsVar), ('color surfaces', self.colorSurfacesVar), ('rad atom style', self.atomStyle), ('rad affect no val', self.doNoValueRadii), ('rad no val', self.noValueRadii), ('worm style', self.wormStyle), ('worm affect no val', self.doNoValueWorm), ('worm no val', self.noValueWorm), ('restrict to sel', self.selRestrictVar) ]: try: widget.set(data[dataName]) except (ValueError, TypeError): pass for dataName, widget in [ ('palette', self.paletteMenu), ('radius', self.radiusEntry) ]: try: widget.setvalue(data[dataName]) except (ValueError, TypeError): pass try: self.selectListBox.setvalue(data['sel list']) except (ValueError, TypeError): pass try: self.noValueWell.showColor(data['no val color'], doCallback=False) except (ValueError, TypeError): pass def _sceneSave(self, trigName, myData, scene): from Animate.Tools import sceneID, colorID info = { 'shown': self.uiMaster().winfo_viewable(), 'target': self.targetMenu.getvalue(), 'models': [sceneID(m) for m in self.modelListBox.getvalue()], 'mode': self.modeNotebook.getcurselection(), 'attribute': self._curAttrMenu().getvalue(), 'hist data': self.histogram().sceneData(), 'sel mode': self.selModeVar.get(), 'sel list': self.selectListBox.getvalue(), 'sel bool': self.selBoolVar.get(), 'action tab': self.renderNotebook.getcurselection(), 'color atoms': self.colorAtomsVar.get(), 'opaque atoms': self.opaqueAtomsVar.get(), 'color ribbons': self.colorRibbonsVar.get(), 'opaque ribbons': self.opaqueRibbonsVar.get(), 'color surfaces': self.colorSurfacesVar.get(), 'no val color': self.noValueWell.rgba, 'palette': self.paletteMenu.getvalue(), 'radius': self.radiusEntry.getvalue(), 'rad atom style': self.atomStyle.get(), 'rad affect no val': self.doNoValueRadii.get(), 'rad no val': self.noValueRadii.get(), 'worm style': self.wormStyle.get(), 'worm affect no val': self.doNoValueWorm.get(), 'worm no val': self.noValueWorm.get(), 'restrict to sel': self.selRestrictVar.get() } scene.tool_settings[self.name] = info def _scalingCB(self): scaling = self.scalingVar.get() prefs[SCALING] = scaling for histogram in [self.renderHistogram, self.selectHistogram]: if scaling == "log": histogram.configure(scaling="logarithmic") else: histogram.configure(scaling="linear") def _selMarkerCB(self, prevMarkers, prevMarker, markers, marker): if prevMarker and prevMarkers != self.renderColorMarkers: self._setRadius(prevMarker) if markers == self.renderColorMarkers: return self.radiusEntry.component('entry').configure(state='normal') if marker is None: self.radiusEntry.clear() self.radiusEntry.component('entry').configure( state='disabled') return if not hasattr(marker, "radius"): # new marker marker.radius = 1.0 self.radiusEntry.setentry("%g" % marker.radius) def _setAttrVals(self, attrVals): index = Modes.index(self.modeNotebook.getcurselection()) self._attrVals[index] = attrVals if attrVals and type(attrVals[0]) in numericTypes: self._minVal[index] = min(attrVals) self._maxVal[index] = max(attrVals) def _setRadius(self, marker): self.radiusEntry.invoke() if not self.radiusEntry.valid(): self.status("Radius value not valid: '%s'" % self.radiusEntry.getvalue(), color='red') return marker.radius = float(self.radiusEntry.getvalue()) def _targetCB(self, menuItem): for o in objectTypes: self.renderAttrsMenu[o].grid_forget() self.selectAttrsMenu[o].grid_forget() self._renderGUI() target = revAttrsLabelMap[menuItem] menus = [self.renderAttrsMenu[target], self.selectAttrsMenu[target]] atomState = "normal" if target.colorAtoms else "disabled" ribbonState = "normal" if target.colorRibbons else "disabled" for menu in menus: menu.grid(row=0, column=0, columnspan=2) menu.component('menubutton').config(text=NO_ATTR) for frame in self.selFrames: frame.grid_forget() for hg in [self.renderHistogram, self.selHistFrame]: hg.grid(row=1, column=0, sticky="nsew") self.renderHistogram['datasource'] = NO_RENDER_DATA self.selectHistogram['datasource'] = NO_SELECT_DATA self.colorAtomsButton.config(state=atomState) self.opaqueAtomsButton.config(state=atomState) self.colorRibbonsButton.config(state=ribbonState) self.opaqueRibbonsButton.config(state=ribbonState) for key in self._attrOkApply.keys(): self._attrOkApply[key] = False self.buttonWidgets['OK'].configure(state='disabled') self.buttonWidgets['Apply'].configure(state='disabled') self.colorKeyButton.configure(state='disabled') self.reverseColorsButton.configure(state='disabled') self.paletteMenu.component('menubutton').config(state="disabled") # Filter models for new object type. self.modelListBox._modelsChange() def _renderGUI(self): target = revAttrsLabelMap[self.targetMenu.getvalue()] page = self.renderNotebook.getcurselection() if page == "Worms": if hasattr(target, 'setWormRadius'): self.wormsWarning.grid_forget() self.wormsFrame.grid() self._renderOkApply = True else: self.wormsFrame.grid_forget() self.wormsWarning.grid() self._renderOkApply = False elif page == "Radii": if hasattr(target, 'setRadius'): self.radiiWarning.grid_forget() self.radiiFrame.grid() self._renderOkApply = True else: self.radiiFrame.grid_forget() self.radiiWarning.grid() self._renderOkApply = False else: self._renderOkApply = True self.renderNotebook.setnaturalsize() state = 'normal' if self._attrOkApply[self.modeNotebook.getcurselection()] and self._renderOkApply else 'disabled' self.buttonWidgets['OK'].configure(state=state) self.buttonWidgets['Apply'].configure(state=state) self.colorKeyButton.configure(state=state) self.reverseColorsButton.configure(state=state) self.paletteMenu.component('menubutton').config(state=state) def _saveAttr(self): from chimera import dialogs d = dialogs.find("SaveAttrDialog", create=True) attrList = self._curAttrMenu().getvalue() if attrList: attrName = attrList[0] else: attrName = None # TODO: If attrList = ['average', 'bfactor'] the current code # does not set the Save dialog attribute menu because # it only gets attrName = 'average'. d.configure(models=self.modelListBox.getvalue(), attrsOf=self.targetMenu.getvalue(), attrName=attrName) d.enter() from chimera import dialogs dialogs.register(ShowAttrDialog.name, ShowAttrDialog) from OpenSave import SaveModeless class SaveAttrDialog(SaveModeless): title = "Save Attribute" name = "SaveAttrDialog" help = "ContributedSoftware/render/render.html#saving" def __init__(self, *args, **kw): self.havePaths = False self.models = [] self.useableTypes = numericTypes self.additionalNumericTypes = () # boolean could be here instead self.additionalOtherTypes = (bool, basestring) SaveModeless.__init__(self, clientPos="s", clientSticky="nsew", *args, **kw) def fillInUI(self, parent): self.targetMenu = None self.modelListBox = None SaveModeless.fillInUI(self, parent) g = Pmw.Group(self.clientArea, tag_text="Attribute to Save") g.pack(expand=Tkinter.TRUE, fill=Tkinter.BOTH) f = Tkinter.Frame(g.interior()) f.pack(side=Tkinter.TOP, expand=Tkinter.FALSE, fill=Tkinter.X) self.attrsMenu = {} for o in objectTypes: self.attrsMenu[o] = CascadeOptionMenu(f, command=lambda mi, o=o: self._compileAttrVals(o, mi), labelpos='w', label_text="Attribute:") self.attrsMenu[objectTypes[0]].grid(row=0, column=0) self.targetMenu = Pmw.OptionMenu(f, command=self._targetCB, items=[o.menuName for o in objectTypes], labelpos='w', label_text=" of") self.targetMenu.grid(row=0, column=1) self.includeModelVar = Tkinter.IntVar(g.interior()) self.includeModelVar.set(0) self.includeModelButton = Tkinter.Checkbutton(g.interior(), pady=0, text="Include model numbers in output", variable=self.includeModelVar) self.includeModelButton.pack(side=Tkinter.BOTTOM) self.saveSelectionVar = Tkinter.IntVar(g.interior()) self.saveSelectionVar.set(0) self.saveSelectionButton = Tkinter.Checkbutton(g.interior(), pady=0, text="Restrict save to current selection, if any", variable=self.saveSelectionVar) self.saveSelectionButton.pack(side=Tkinter.BOTTOM) from chimera.widgets import ModelScrolledListBox self.modelListBox = ModelScrolledListBox(g.interior(), selectioncommand=lambda: self.configure( models=self.modelListBox.getvalue(), fromModelListBox=True), filtFunc = self.filterModels, listbox_selectmode="extended", labelpos="nw", label_text="Models") self.modelListBox.pack(side=Tkinter.BOTTOM, expand=Tkinter.TRUE, fill=Tkinter.BOTH) self.targetMenu.invoke(attrsPrefMap[prefs[TARGET]].menuName) def filterModels(self, model): target = revAttrsLabelMap[self.targetMenu.getvalue()] return isinstance(model, target.modelType) def Apply(self): paths = self.getPaths(remember=False) filename = paths[0] models = self.modelListBox.getvalue() if not models: raise chimera.UserError("No models selected") restrict = (self.saveSelectionVar.get() and not selection.currentEmpty()) target = revAttrsLabelMap[self.targetMenu.getvalue()] menu = self.attrsMenu[target] if restrict: selObj = target.selectedObjects() objList = target.objectsInModels(selObj, models) target.sortObjects(objList) else: objList = target.modelObjects(models) if not objList: raise chimera.UserError("No items selected") attrList = menu.getvalue() if not attrList: raise chimera.UserError("No attribute selected") attrName = attrList[-1] if self.includeModelVar.get(): level = selection.SelGraph else: level = selection.SelSubgraph f = open(filename, "w") try: if len(attrList) == 1: print >> f, "attribute:", attrName print >> f, "recipient:", target.menuName # direct attribute, just print for o in objList: try: val = getattr(o, attrName) except AttributeError: continue if val is not None: print >> f, "\t%s\t%s" % ( target.objectId(o,level), repr(val)) else: # averaged attribute, need to be more clever print >> f, "attribute:", '_'.join(attrList) print >> f, "recipient:", target.menuName from operator import add for o in objList: vals = [] for sub in target.childObjects(o): try: val = getattr(sub, attrName) except AttributeError: continue if val is not None: vals.append(val) if vals: val = reduce(add, vals) if not summableAttrName( attrName): val /= float(len(vals)) print >> f, "\t%s\t%g" % ( target.objectId(o,level), val) finally: f.close() msg = "Attribute %s of %s saved in file %s" % (attrName, target.menuName, filename) from chimera import replyobj replyobj.status(msg) def configure(self, models=None, attrsOf=None, attrName=None, fromModelListBox=False): curMenu = self._curAttrMenu() curAttr = curMenu.getvalue() curTargetLabel = self.targetMenu.getvalue() curTarget = revAttrsLabelMap[curTargetLabel] refreshedAttrs = False if attrsOf != curTargetLabel and attrsOf is not None: self.targetMenu.invoke(attrsOf) if models != self.models and models is not None: newModels = None if fromModelListBox: from sets import Set oldSet = Set(self.models) newSet = Set(models) if newSet >= oldSet: newModels = newSet - oldSet else: self.modelListBox.setvalue(models, doCallback=False) self.models = models self._populateAttrsMenus(newModels=newModels) refreshedAttrs = True if not models or (curAttr is None and attrName is None): # _populateAttrsMenu has knocked the menu # button off of "choose attr"; arrange for # it to be restored... attrName = NO_ATTR if (attrsOf == curTargetLabel or attrsOf is None) \ and ([attrName] == curAttr or attrName is None): # no other changes if curAttr is not None: try: curMenu.invoke(curAttr) except ValueError: # attribute no longer present self._targetCB(curTargetLabel) # if an attribute name is specified, probably want an update... if attrName is not None: if attrName == NO_ATTR: self._targetCB(curTargetLabel) else: target = curTarget if attrsOf is None else revAttrsLabelMap[attrsOf] seen = self.seenAttrs[target] if not refreshedAttrs and attrName not in seen: self._populateAttrsMenus() # 'seen' pointing to old list now... seen = self.seenAttrs[target] if attrName in seen: self._curAttrMenu().invoke([attrName]) elif not attrsOf is None: self._targetCB(attrsOf) self._attrReady() def _compileAttrVals(self, target, menuItem): self.status("Surveying attribute %s" % " ".join(menuItem), blankAfter=0) attrVals = [] surveyed = target.modelObjects(self.models) attrName = menuItem[-1] hasNone = False if len(menuItem) == 1: for t in surveyed: try: val = getattr(t, attrName) except AttributeError: hasNone = True continue if val is None or (isinstance(val, basestring) and not val): hasNone = True continue attrVals.append(val) else: # average of atoms/residues from operator import add for t in surveyed: vals = [] for sub in target.childObjects(t): try: val = getattr(sub, attrName) except AttributeError: continue if val is None \ or (isinstance(val, basestring) and not val): continue vals.append(val) if not vals: hasNone = True continue val = reduce(add, vals) if not summableAttrName(attrName): val /= float(len(vals)) attrVals.append(val) self.status("Done surveying") self._attrReady() def _composeAttrMenus(self): sortFunc = lambda a, b: cmp(a.lower(), b.lower()) for o in objectTypes: self.useableAttrs[o].sort(sortFunc) # do this in a little bit of a weird order so that the 'average' # submenu only contains numeric quantities aggAveAttrs = {None:[]} for o in objectTypes: aggAveAttrs[o] = self.useableAttrs[o] \ + self.additionalNumericAttrs[o] aggAveAttrs[o].sort(sortFunc) for o in objectTypes: aggAttrs = aggAveAttrs[o] + self.additionalOtherAttrs[o] aggAttrs.sort(sortFunc) self.attrsMenu[o].setitems(_menuItems( aggAttrs, aggAveAttrs[o.childType()], o.screenedSubAttrs)) def _curAttrMenu(self): if not self.targetMenu: return None target = revAttrsLabelMap[self.targetMenu.getvalue()] return self.attrsMenu[target] def _populateAttrsMenus(self, newModels=None): self.status("Compiling attribute lists...", blankAfter=0) if newModels is None or not hasattr(self, 'seenAttrs'): self.seenAttrs = {} self.useableAttrs = {} self.additionalNumericAttrs = {} self.additionalOtherAttrs = {} for o in objectTypes: self.seenAttrs[o] = o.screenedAttrs.copy() self.useableAttrs[o] = [] self.additionalNumericAttrs[o] = [] self.additionalOtherAttrs[o] = [] if newModels is None: scanModels = self.models else: scanModels = newModels for o in objectTypes: self.status("Compiling attribute lists for %s..." % o.menuName, blankAfter=0) models = [m for m in scanModels if isinstance(m, o.modelType)] for item in o.modelObjects(models): self._reapAttrs(item, self.seenAttrs[o], self.useableAttrs[o], self.additionalNumericAttrs[o], self.additionalOtherAttrs[o]) self.status("Updating attribute menus...", blankAfter=0) self._composeAttrMenus() self.status("") def _reapAttrs(self, instance, seenAttrs, useableAttrs, additionalNumericAttrs, additionalOtherAttrs): for attrName in dir(instance): attrType = seenAttrs.get(attrName, None) if isinstance(attrType, bool): continue elif attrType is None: if attrName[0] == '_' \ or attrName[0].isupper(): # e.g. 'Ball' seenAttrs[attrName] = False continue attr = getattr(instance, attrName) if attr is None or (isinstance(attr, basestring) and not attr): # defer judgment until # we see a real value continue attrType = type(attr) else: attr = attrType() seenAttrs[attrName] = True if attrType in self.useableTypes: useableAttrs.append(attrName) elif attrType in self.additionalNumericTypes: additionalNumericAttrs.append(attrName) elif isinstance(attr, self.additionalOtherTypes): additionalOtherAttrs.append(attrName) def _targetCB(self, menuItem): for o in objectTypes: self.attrsMenu[o].grid_forget() target = revAttrsLabelMap[menuItem] menu = self.attrsMenu[target] menu.grid(row=0, column=0) menu.component('menubutton').config(text=NO_ATTR) SaveModeless._millerReady(self, None) # Filter models for new object type. self.modelListBox._modelsChange() def _millerReady(self, paths): if not self.modelListBox or not self.modelListBox.getvalue(): paths = None else: menu = self._curAttrMenu() if menu: attrList = menu.getvalue() if not attrList: paths = None else: paths = None SaveModeless._millerReady(self, paths) def _attrReady(self): self._millerReady(self.getPaths(remember=False)) _attrNameAnalysisCache = { "accessibleSurface": True, # Attribute from "Area/Volume from Web" } def summableAttrName(attrName): # split camel case and underscored names if attrName not in _attrNameAnalysisCache: components = [] start = 0 for i in range(1, len(attrName)): p, c = attrName[i-1:i+1] if c == "_": if p != "_": components.append( attrName[start:i].lower()) start = i+1 elif p.islower() and c.isupper(): components.append(attrName[start:i].lower()) start = i elif p.isupper() and c.islower() and start < i-1: # only final cap of a stretch is part of the camel case components.append(attrName[start:i-1].lower()) start = i-1 if start < len(attrName): components.append(attrName[start:].lower()) for summable in ("area", "volume", "charge"): if summable in components: _attrNameAnalysisCache[attrName] = True break else: _attrNameAnalysisCache[attrName] = False return _attrNameAnalysisCache[attrName] def _menuItems(baseItems, subItems, screenedSubAttrs): items = baseItems for summable, label in [(False, "average"), (True, "total")]: filtered = [si for si in subItems if summableAttrName(si) == summable and si not in screenedSubAttrs] if filtered: items = items + [(label, filtered)] return items