# --- 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: RmsdMap.py 36306 2012-04-26 20:54:25Z pett $ import Tkinter, Pmw from chimera.baseDialog import ModelessDialog from chimera import UserError from prefs import prefs, RES_NET_WEIGHTING, RES_NET_IGNORE_BONDED, \ RES_NET_SOLVENT_NODE, RES_NET_IONS_NODE, RES_NET_EDGE_DISCARD_FRAC, \ RES_NET_EDGE_DISCARD_WEIGHT, RES_NET_EDGE_HIST_MARKERS, RES_NET_EDGE_WIDTH, \ RES_NET_INTERACTION_TYPE class ResInteractionStarter(ModelessDialog): title = "Get Residue Interaction Parameters" help = "ContributedSoftware/movie/movie.html#rins" def __init__(self, movie, clusterInfo=None, differenceNetwork=False): self.movie = movie self.clusterInfo = clusterInfo self.differenceNetwork = differenceNetwork self.paramDialogs = [] if clusterInfo: self.oneshot = True self.title = "Calculate Residue-Interaction Network for Cluster" else: self.title = "Calculate Residue-Interaction Network for Trajectory" movie.subdialogs.append(self) ModelessDialog.__init__(self) def fillInUI(self, parent): parent.columnconfigure(0, weight=1) import itertools row = itertools.count() from CGLtk.WrappingLabel import WrappingLabel disclaimer = WrappingLabel(parent, text= "To show the network you need to start" " Cytoscape first and then run Chimera via Cytoscape's StructureViz plugin.", fg="#ff0000") from CGLtk.Font import shrinkFont shrinkFont(disclaimer, fraction=1.25) disclaimer.grid(row=row.next(), column=0, columnspan=2, sticky="ew") if self.differenceNetwork: WrappingLabel(parent, text="Compute the difference between the" " residue-interaction networks for two clusters. OK" " will bring up a dialog for specifying contact parameters.", relief="ridge", bd=4).grid(row=row.next(), column=0, columnspan=2, sticky="ew") else: if self.clusterInfo: addendum = "" else: addendum = " To compute an interaction network for a cluster," \ " first calculate clusters using the Analysis menu, then use" \ " the cluster dialog residue-interaction network option." WrappingLabel(parent, text="Compute residue-interaction network. OK" " will bring up a dialog for specifying contact parameters.%s" % addendum, relief="ridge", bd=4).grid(row=row.next(), column=0, columnspan=2, sticky="ew") from chimera.tkoptions import IntOption, BooleanOption, FloatOption, EnumOption if not self.clusterInfo: startFrame = self.movie.startFrame endFrame = self.movie.endFrame self.startFrame = IntOption(parent, row.next(), "Starting frame", startFrame, None, min=startFrame, max=endFrame, width=6) numFrames = endFrame - startFrame + 1 defStride = 1 + int(numFrames/300) self.stride = IntOption(parent, row.next(), "Step size", defStride, None, min=1, max=numFrames, width=3) self.endFrame = IntOption(parent, row.next(), "Ending frame", endFrame, None, min=startFrame, max=endFrame, width=6) class InteractionTypeOption(EnumOption): values = ["H-bonds", "contacts"] self.interactionType = InteractionTypeOption(parent, row.next(), "Type of residue interactions", prefs[RES_NET_INTERACTION_TYPE], None) self.doWeight = BooleanOption(parent, row.next(), "Weight interactions" " by number of H-bonds/contacts formed", prefs[RES_NET_WEIGHTING], self._weightingChange) self.ignoreBonded = BooleanOption(parent, row.next(), "Ignore H-bonds/contacts" " between covalently bonded residues", prefs[RES_NET_IGNORE_BONDED], None) self.lumpSolvent = BooleanOption(parent, row.next(), "Group solvent as" " one node", prefs[RES_NET_SOLVENT_NODE], None) self.lumpIons = BooleanOption(parent, row.next(), "Group ions as" " one node", prefs[RES_NET_IONS_NODE], None) lowRow = row.next() self.lowValueCutoff = FloatOption(parent, lowRow, "Discard-edge threshold", prefs[RES_NET_EDGE_DISCARD_FRAC], None, balloon="Omit residue interactions" " that occur in less than the given fraction of frames") self.lowValueCutoff.forget() self.lowWeightCutoff = FloatOption(parent, lowRow, "Weighted discard-edge" " threshold", prefs[RES_NET_EDGE_DISCARD_WEIGHT], None, balloon= "Omit residue interactions whose final weight is less than the given value.") self.lowWeightCutoff.forget() # due to the way the .forget() method works, can't manage both and then # forget the one we don't want... if self.doWeight.get(): self.lowWeightCutoff.manage() else: self.lowValueCutoff.manage() def Apply(self): if self.clusterInfo: if self.differenceNetwork: info1, info2 = self.clusterInfo frames = (info1[0], info2[0]) rep = (info1[1], info2[1]) else: frames, rep = self.clusterInfo else: startFrame = self.startFrame.get() stride = self.stride.get() endFrame = self.endFrame.get() if endFrame <= startFrame: self.enter() raise UserError("Start frame must be less" " than end frame") if startFrame < self.movie.startFrame \ or endFrame > self.movie.endFrame: self.enter() raise UserError("Start or end frame outside of trajectory") frames = range(startFrame, endFrame+1, stride) rep = None class ParamsBase: buttons = ("OK", "Cancel") oneshot = True mdresnet_frames = frames mdresnet_representative = rep mdresnet_movie = self.movie mdresnet_doWeight = self.doWeight.get() mdresnet_ignoreBonded = self.ignoreBonded.get() mdresnet_lumpSolvent = self.lumpSolvent.get() mdresnet_lumpIons = self.lumpIons.get() if self.paramDialogs is None: mdresnet_container = self.movie.subdialogs else: mdresnet_container = self.paramDialogs if mdresnet_doWeight: mdresnet_edgeCutoff = self.lowWeightCutoff.get() else: mdresnet_edgeCutoff = self.lowValueCutoff.get() mdresnet_userClosed = False def Apply(slf, *args, **keywds): cmdArgs, cmdKeywds = slf.mdresnet_gatherArgsKeywds() ResInteractionDialog(slf.mdresnet_movie, slf.mdresnet_frames, slf.mdresnet_representative, slf.mdresnet_doWeight, slf.mdresnet_ignoreBonded, slf.mdresnet_lumpSolvent, slf.mdresnet_lumpIons, slf.mdresnet_edgeCutoff, slf.__class__.mdresnet_getInteractions, cmdArgs, cmdKeywds) def Close(slf): # not being directly destroyed... slf.mdresnet_userClosed = True try: slf.__class__.__bases__[-1].Close(slf) except: slf.mdresnet_userClosed = False raise def destroy(slf): if slf.mdresnet_userClosed: # can't do this directly in Close because some settings # call self.enter() and then raise an error, so Close # may be called multiple times slf.mdresnet_container.remove(slf) if self.interactionType.get() == "H-bonds": prefs[RES_NET_INTERACTION_TYPE] = "H-bonds" from FindHBond.gui import HBDialog class HBondParams(ParamsBase, HBDialog): title = "Residue Interaction H-Bond Parameters" help = "ContributedSoftware/movie/movie.html#rin-hbonds" MDResNetMode = True def destroy(slf): for base in slf.__class__.__bases__: base.destroy(slf) def mdresnet_gatherArgsKeywds(slf): from chimera import UserError if slf.relaxParams.relaxConstraints: try: kw = { 'distSlop': slf.relaxParams.relaxDist, 'angleSlop': slf.relaxParams.relaxAngle } except UserError: slf.enter() raise else: kw = { 'distSlop': 0.0, 'angleSlop': 0.0 } kw['intermodel'] = False kw['intramodel'] = True kw['cacheDA'] = True if slf.inSelection.get(): from chimera.selection import currentAtoms selAtoms = [a for a in currentAtoms() if a.molecule == slf.mdresnet_movie.model._mol] if not selAtoms: slf.enter() raise UserError("No trajectory atoms selected!") selRestrict = slf.SelShorthands[slf.selType.index(Pmw.SELECT)] import weakref slf.__class__.mdresnet_hb_selAtoms = weakref.WeakSet(selAtoms) if selRestrict == "both": kw['donors'] = kw['acceptors'] = \ slf.__class__.mdresnet_hb_selAtoms elif selRestrict == "osl": selRestrict = slf.STAtomSpec.get() else: selRestrict = None slf.__class__.mdresnet_hb_selRestrict = selRestrict return ([slf.mdresnet_movie.model._mol],), kw @classmethod def mdresnet_getInteractions(cls, *args, **kw): crdSet = kw.pop('crdSet') from FindHBond import findHBonds, filterHBondsBySel try: # since getting findHBonds to use a coord set other # than the current coordinate set would be a massive # undertaking, use this horrible kludge to get it # to use another coord set def kludge(slf, cs=crdSet): return slf.mdresnet_xformCoord(cs) from chimera import Atom Atom.mdresnet_xformCoord = Atom.xformCoord Atom.xformCoord = kludge hbonds = findHBonds(*args, **kw) finally: Atom.xformCoord = Atom.mdresnet_xformCoord delattr(Atom, 'mdresnet_xformCoord') if cls.mdresnet_hb_selRestrict is not None: hbonds = filterHBondsBySel(hbonds, cls.mdresnet_hb_selAtoms, cls.mdresnet_hb_selRestrict) return hbonds dlg = HBondParams() else: prefs[RES_NET_INTERACTION_TYPE] = "contacts" from DetectClash.gui import DetectClashDialog class ContactParams(ParamsBase, DetectClashDialog): title = "Residue Interaction Contact Parameters" help = "ContributedSoftware/movie/movie.html#rin-contacts" IncludeTreatment = False IncludeFrequency = False def destroy(slf): if slf.designated: slf.designated.selChangedCB = None if slf.designated2: slf.designated2.selChangedCB = None for base in slf.__class__.__bases__: base.destroy(slf) def mdresnet_gatherArgsKeywds(slf): return DetectClashDialog.gatherArgsKeywds(slf) @classmethod def mdresnet_getInteractions(cls, *args, **kw): from DetectClash import cmdDetectClash data = cmdDetectClash(*args, **kw) return [(a1, a2) for a1, aContacts in data.items() for a2 in aContacts.keys()] dlg = ContactParams() dlg._contactDefaultsCB() if self.paramDialogs is None: self.movie.subdialogs.append(dlg) else: self.paramDialogs.append(dlg) def Close(self): # if we are one-shot, transfer responsibility for param # dialogs to main gui if self.clusterInfo: self.movie.subdialogs.extend(self.paramDialogs) for pd in self.paramDialogs: pd.mdresnet_container = self.movie.subdialogs self.paramDialogs = None ModelessDialog.Close(self) def destroy(self): if self.paramDialogs is not None: for pd in self.paramDialogs: pd.destroy() self.paramDialogs = None self.movie = None ModelessDialog.destroy(self) def _weightingChange(self, opt): if opt.get(): self.lowValueCutoff.forget() self.lowWeightCutoff.manage() else: self.lowWeightCutoff.forget() self.lowValueCutoff.manage() class ResInteractionDialog(ModelessDialog): title = "Residue Interaction Computation and Display" oneshot = True provideStatus = True statusPosition = "above" buttons = ("OK", "Apply", "Close") help = "ContributedSoftware/movie/movie.html#rin-colors" def __init__(self, movie, frames, representative, doWeight, ignoreBonded, lumpSolvent, lumpIons, edgeCutoff, interactionsCmd, interactionsArgs, interactionsKeywds): self.movie = movie self.movie.subdialogs.append(self) self.frames = frames self.representative = representative self.ignoreBonded = ignoreBonded self.doWeight = doWeight self.lumpSolvent = lumpSolvent self.lumpIons = lumpIons self.edgeCutoff = edgeCutoff self.interactionsCmd = interactionsCmd self.interactionsArgs = interactionsArgs self.interactionsKeywds = interactionsKeywds ModelessDialog.__init__(self) def Close(self): if self._computing: self._abort = True return self.movie.subdialogs.remove(self) ModelessDialog.Close(self) def destroy(self): self.movie = None ModelessDialog.destroy(self) def fillInUI(self, parent): from chimera import numpyArrayFromAtoms, UserError self._computing = False self.buttonWidgets['OK'].configure(state="disabled") self.buttonWidgets['Apply'].configure(state="disabled") self.buttonWidgets['Close'].configure(text="Abort") prefs[RES_NET_WEIGHTING] = doWeight = self.doWeight prefs[RES_NET_IGNORE_BONDED] = ignoreBonded = self.ignoreBonded if doWeight: prefs[RES_NET_EDGE_DISCARD_WEIGHT] = self.edgeCutoff else: prefs[RES_NET_EDGE_DISCARD_FRAC] = self.edgeCutoff from itertools import count row = count() from CGLtk.Histogram import MarkedHistogram self.histogram = MarkedHistogram(parent, statusline=self.status, minlabel=True, maxlabel=True, scaling="linear", labelpos='w', label_text="Edge weight\nto edge color\nmapping") self.histogram['datasource'] = "Computing residue interactions" self.histogram.grid(row=row.next(), column=0, columnspan=2, sticky="nsew") from chimera.tkoptions import SymbolicEnumOption, StringOption class EdgeWidthOption(SymbolicEnumOption): values = list(range(1, 11)) labels = [str(v) for v in values] self.edgeWidthOption = EdgeWidthOption(parent, row.next(), "Edge width", prefs[RES_NET_EDGE_WIDTH], None, balloon="How thick edges the edges" " between nodes in the Cytoscape network are") self.netNameOption = StringOption(parent, row.next(), "Network name", "MD residue interactions", None, balloon="Title of network in Cytoscape" " as well as name of corresponding visual style") from collections import defaultdict self.differenceNetwork = self.representative is not None \ and type(self.representative) != int if self.differenceNetwork: frameSets = self.frames else: frameSets = [self.frames] networkInfo = [] for fsi, frameSet in enumerate(frameSets): numInteractions = defaultdict(int) for fni, frameNum in enumerate(frameSet): self._computing = True self._abort = False parent.update() # allow abort self._computing = False if self._abort: parent.after_idle(self.Close) return # load needed coord sets... if len(frameSets) > 1: statusInfo = "Frame %d (%d/%d) of cluster %d: " % (frameNum, fni+1, len(frameSet), fsi+1) else: statusInfo = "Frame %d (%d/%d): " % (frameNum, fni+1, len(frameSet)) if not self.representative and not self.movie.findCoordSet(frameNum): self.status(statusInfo + "loading") self.movie._LoadFrame(frameNum, makeCurrent=False) cs = self.movie.findCoordSet(frameNum) self.status(statusInfo + "finding interactions") self.interactionsKeywds['crdSet'] = cs interactions = self.interactionsCmd(*self.interactionsArgs, **self.interactionsKeywds) self.status(statusInfo + "processing interactions") from chimera import bondsBetween if not doWeight: resPairSeen = set() atomPairSeen = set() for a1, a2 in interactions: if (a1, a2) in atomPairSeen: continue atomPairSeen.add((a1, a2)) atomPairSeen.add((a2, a1)) r1, r2 = a1.residue, a2.residue if r1 == r2: continue if a1.surfaceCategory in ("solvent", "ions") \ and a2.surfaceCategory in ("solvent", "ions"): continue if ignoreBonded and bondsBetween(r1, r2, onlyOne=True): continue if self.lumpSolvent and a1.surfaceCategory == "solvent": resID1 = "solvent" elif self.lumpIons and a1.surfaceCategory == "ions": resID1 = "ions" else: resID1 = r1 if self.lumpSolvent and a2.surfaceCategory == "solvent": resID2 = "solvent" elif self.lumpIons and a2.surfaceCategory == "ions": resID2 = "ions" else: resID2 = r2 if str(resID1) < str(resID2): key = (resID1, resID2) else: key = (resID2, resID1) if not doWeight: if key in resPairSeen: continue resPairSeen.add(key) numInteractions[key] += 1 numFrames = float(len(frameSet)) for interaction in numInteractions.keys(): numInteractions[interaction] /= numFrames networkInfo.append(numInteractions) self.buttonWidgets['Close'].configure(text="Close") self.interactions = {} if self.differenceNetwork: allInteractions = set() inter1, inter2 = networkInfo allInteractions.update(inter1.keys()) allInteractions.update(inter2.keys()) for interaction in allInteractions: val1 = inter1.get(interaction, 0.0) val2 = inter2.get(interaction, 0.0) if val1 < self.edgeCutoff and val2 < self.edgeCutoff: continue self.interactions[interaction] = val2 - val1 else: for interaction, val in networkInfo[0].items(): if val < self.edgeCutoff: continue self.interactions[interaction] = val if self.interactions: data = self.interactions.values() minVal, maxVal = min(data), max(data) # need to put data into histogram bins binSize = 1.0 / len(frameSet) vrange = (maxVal - minVal) numBins = int(vrange/binSize + 0.5) + 1 if numBins == 1: bins = [len(data)] else: bins = [0] * numBins binSize = vrange / float(numBins - 1) leftEdge = minVal - 0.5 * binSize for val in data: bin = int((val - leftEdge) / binSize) bins[bin] += 1 self.histogram['datasource'] = (minVal, maxVal, bins) self.markers = self.histogram.addmarkers(coordtype="absolute") self.markers.extend(prefs[RES_NET_EDGE_HIST_MARKERS][self.differenceNetwork] [doWeight]) self.buttonWidgets['OK'].configure(state="normal") self.buttonWidgets['Apply'].configure(state="normal") self.status("Click OK or Apply to show network in Cytoscape") else: self.histogram['datasource'] = "No interacting residues" def Apply(self): import os, tempfile nodes = set() netName = self.netNameOption.get() cytoInfo = [2, netName] fd, path = tempfile.mkstemp(suffix='_network.txt', text=True) cytoInfo.append(path) f = os.fdopen(fd, "w") for interaction, val in self.interactions.items(): r1, r2 = interaction print>>f, "%s\tvdw\t%s\t%g" %(r1, r2, val) nodes.add(r1) nodes.add(r2) f.close() fd, path = tempfile.mkstemp(suffix='_nattr.txt', text=True) cytoInfo.append(path) f = os.fdopen(fd, "w") for node in nodes: if isinstance(node, basestring): print>>f, "%s\t/surfaceCategory=%s" % (node, node) else: ident = node.oslIdent() print>>f, "%s\t%s\t%s" % (node, ident[ident.index(':')+1:], node.molecule.name) f.close() markerSettings = [(m['xy'], m['rgba']) for m in self.markers] oldMarkerPref = prefs[RES_NET_EDGE_HIST_MARKERS] newMarkerPref = [] for diff in range(2): subPref = [] for weighted in range(2): if diff == self.differenceNetwork and weighted == self.doWeight: subPref.append(markerSettings) else: subPref.append(oldMarkerPref[diff][weighted]) newMarkerPref.append(tuple(subPref)) prefs[RES_NET_EDGE_HIST_MARKERS] = tuple(newMarkerPref) edgeWidth = prefs[RES_NET_EDGE_WIDTH] = self.edgeWidthOption.get() fd, path = tempfile.mkstemp(suffix='_netviz.xml', text=True) cytoInfo.append(path) f = os.fdopen(fd, "w") print>>f, netvizPreamble % netName indent1 = " " print>>f, indent1 + "" indent2 = indent1 + " " prefix = indent2 + '>f, '%s%s" default="%s"/>' % (prefix, name, val) for coord in "XYZ": print>>f, '%sCENTER_%s_LOCATION" default="0.0"/>' % (prefix, coord) print>>f, indent1 + "" print>>f, indent1 + "" print>>f, indent2 + '' print>>f, indent2 + '' prefix = indent2 + '>f, '%sBORDER_%s" default="%s"/>' % (prefix, name, val) for n in range(1, 10): print>>f, '%sCUSTOMGRAPHICS_%d" default="org.cytoscape.ding.customgraphics.NullCustomGraphics,0,[ Remove Graphics ],"/>' % (prefix, n) print>>f, '%sCUSTOMGRAPHICS_POSITION_%d" default="C,C,c,0.00,0.00"/>' % (prefix, n) print>>f, '%sCUSTOMGRAPHICS_SIZE_%d" default="50.0"/>' % (prefix, n) print>>f, '%sCUSTOMPAINT_%d" default="DefaultVisualizableVisualProperty(id=NODE_CUSTOMPAINT_2, name=Node Custom Paint %d)"/>' % (prefix, n, n) for name, val in [("DEPTH", "0.0"), ("FILL_COLOR", "#00acad"), ("HEIGHT", "40.0"), ("NESTED_NETWORK_IMAGE_VISIBLE", "true"), ("PAINT", "#1e90ff"), ("SELECTED", "false"), ("SELECTED_PAINT", "#ffff00"), ("SHAPE", "ROUND_RECTANGLE"), ("SIZE", "45.0"), ("TOOLTIP", ""), ("TRANSPARENCY", "255"), ("VISIBLE", "true"), ("WIDTH", "70.0")]: print>>f, '%s%s" default="%s"/>' % (prefix, name, val) print>>f, '%sLABEL" default="">' % prefix print>>f, indent2 + ' ' print>>f, indent2 + '' for name, val in [("COLOR", "#000000"), ("FONT_FACE", "Dialog,plain,12"), ("FONT_SIZE", "12"), ("POSITION", "C,C,c,0.00,0.00"), ("TRANSPARENCY", "255"), ("WIDTH", "200.0")]: print>>f, '%sLABEL_%s" default="%s"/>' % (prefix, name, val) for coord in "XYZ": print>>f, '%s%s_LOCATION" default="0.0"/>' % (prefix, coord) print>>f, indent1 + "" print>>f, indent1 + "" print>>f, indent2 + '' prefix = indent2 + '>f, '%s%s" default="%s"/>' % (prefix, name, val) print>>f, '%sWIDTH" default="%.1f"/>' % (prefix, edgeWidth) print>>f, '%sSTROKE_UNSELECTED_PAINT" default="#333333">' % (prefix,) print>>f, indent2 + ' ' from CGLtk.color import rgba2tk for xy, rgba in markerSettings: tkColor = rgba2tk(rgba, fieldWidth=2) print>>f, '%s ' % (indent2, tkColor, tkColor, tkColor, xy[0]) print>>f, indent2 + ' ' print>>f, indent2 + '' for name, val in [("COLOR", "#000000"), ("FONT_FACE", "Dialog,plain,10"), ("FONT_SIZE", "10"), ("TRANSPARENCY", "255")]: print>>f, '%sLABEL_%s" default="%s"/>' % (prefix, name, val) for name, val in [("SELECTED_PAINT", "#ffff00"), ("SHAPE", "NONE"), ("UNSELECTED_PAINT", "#000000")]: print>>f, '%sSOURCE_ARROW_%s" default="%s"/>' % (prefix, name, val) print>>f, '%sTARGET_ARROW_%s" default="%s"/>' % (prefix, name, val) print>>f, indent1 + "" print>>f, netvizPostmortem f.close() from sys import __stdout__ as stdout print>>stdout, "Trajectory residue network info: %s" % repr(cytoInfo) stdout.flush() netvizPreamble = """ """ netvizPostmortem = """ """