Dear Pymollers, Thanks to those of you that had suggestions for my problems getting rTools-like features in MacPymol. I wanted to let you know that I just received a perfect workaround that should probably be circulated to those that are using stand-alone Pymol for the Mac where rTools is not available. The suggestion was from Tsjerk Wassenaar who suggested his and Kristain RotherĀ¹s script called movie.py (evidently the predecessor to rTools, but has essentially all of the same movie features). The dialog with him, including the movie.py script, is included in the email below. If the script is in the same directory as the PDB file and the xxx.pml script and is called from the xxx.pml script then all of the rTools movie features are now available in the xxx.pml animation script. It works great!!! Now Pymollers on the Mac have the best features of the Pymol Fink install with rTools as well as the Mac-specific acceleration and Aqua interface of MacPymol.
Thanks to Warren and Tsjerk for bringing the cutting edge Pymol features to those of us poor Mac-ophiles. Best regards, Kelley ***************** Note: the following emails are in reverse chronological order ***************** Dear Tsjerk, It worked marvelously!!! As you stated It just needs to be in the same directory as the PDB file and the xxx.pml animation script file. I added the "run movie.py" line into the xxx.pml script file and just ran the script and voila, it works just like rTools!!! THANK YOU, THANK YOU, THANK YOU!!! I just did an animation script in 10 min that took me all day yesterday. All of this should probably be forwarded to the Pymol email listserv for the other dunces out there like me. Bets regards, Kelley ********************* Hi Kelley, The file doesn't need to be in a particular directory, but Pymol should know where it is. So you will either have to give the full path to the file, or you may try to put it in the same directory where you have the pdb file you are using. It should also be possible to have an initialization file for pymol in which you can put it, to be loaded every time you start pymol, but I wouldn't know that for Mac (which you were using, weren't you?), though that has been on the list earlier if I recall correctly. I hope it works. Succes. Tsjerk ********************* Hi Tsjerk, The script looks interesting and I would like to try it but I am a little confused on how to execute the script. I cut and pasted the script into a text file that I called movie.py and placed it in the Applications directory. Attempts to run movie.py or load movie.py gave error messages like the following: run movie.py Traceback (most recent call last): File "/Users/delwarl/pymol/products/MacPyMOL.app/pymol/modules/pymol/parser.py", line 206, in parse execfile(exp_path(args[nest][0]),pymol_names,pymol_names) IOError: [Errno 2] No such file or directory: 'movie.py' I am sure that I am doing something naively stupid, but I could use some help on this. Is there a particular directory where the script needs to be? Did I do the right thing in creating a text file called movie.py containing the script? How am I supposed to load the script into Pymol? Thanks for your help. Regards, Kelley ***************************** Hi Kelley, Maybe the pre-rTools movie.py, mainly written by Kristian too, with some extensions of my own, can be of some help. Hope it does the trick, good luck. Tsjerk -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- :) -- :) Tsjerk A. Wassenaar, M.Sc. -- :) Molecular Dynamics Group -- :) Dept. of Biophysical Chemistry -- :) University of Groningen -- :) Nijenborgh 4 -- :) 9747 AG Groningen -- :) The Netherlands -- :) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- :) -- :) Hi! I'm a .signature virus! -- :) Copy me into your ~/.signature to help me spread! -- :) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ """ --- Movie: easy movie scripting in PyMOL --- Script : Movie Author : Kristian Rother Revision: Tsjerk Wassenaar Date : 11.11.2003 Version : 0.2 Contact : kristian.rot...@berlin.de t.a.wassen...@chem.rug.nl Copyright (C) 2002 K. Rother Movie is an extension for the PyMOL Molecular Graphics System. It provides lots of new commands for PyMOL: movie - plays a movie mvClear - see descriptions below mvMove mvRot mvCxRot mvSet mvCmd mvGradCol mvSinrot mvSinmove mvSinset mvView Translation and rotation commands are assigned to frame ranges, resulting in a much shorter scripting of animations. Additionaly, smooth movements are enabled by the mvSinxxx commands. See example scripts. Usage: run from PyMOL or .pymolrc Literature: DeLano, W.L. The PyMOL Molecular Graphics System (2002) DeLano Scientific, San Carlos, CA, USA. http://www.pymol.org ---------------------------------------------------------------------- Movie for PyMOL is an OPEN SOURCE program distributed under the "Python" license. Please see the LICENSE file for additional information. ---------------------------------------------------------------------- """ class mvMovie: def __init__(self): """Permanently stores what should appear in the movie.""" self.movie=[] self.backmovie=[] mv = mvMovie() # import some python modules from pymol import cmd import string from math import * from copy import deepcopy # --------------------------------------------------------- # internal stuff def parseFrames(frames): """Returns a tuple of first and last frame from a string like 1-10; 10; 99-200; 5; """ firstFrame=1 lastFrame=1 if string.find(frames,"-")>=0: # range info given t=string.split(frames,"-") firstFrame=int(t[0]) lastFrame=int(t[1]) else: # no range specified firstFrame=int(frames) lastFrame=firstFrame return (firstFrame,lastFrame) # ------------------------------------- # here come the commands def mvClear(): """Deletes the movie.""" mv.movie=[] mv.backmovie=[] cmd.mclear() cmd.frame(1) def mvRot(frames="1",axis="z",totalAngle=360): """ mvRot(frames,axis,totalAngle) - rotates the environment over the given frame range, the rotation angle summing up to the totalAngle given. """ frameRange = parseFrames(frames) nFrames = frameRange[1]-frameRange[0]+1 angleIncrement = float(totalAngle)/(1.0*nFrames) i=frameRange[0] while i<=frameRange[1]: mv.movie.append((i,"turn %s,%f"%(axis,angleIncrement))) i+=1 def mvCxRot(frames="1",X=0,Y=0,Z=0): """ mvCxRot(frames,Xangle,Yangle,Zangle) - Rotates the scene gradually over the three axes, corresponding to Euler angle rotations; For the end scene, consider rotation first around z-axis, then around y and finally around x. """ frameRange = parseFrames(frames) nFrames = frameRange[1]-frameRange[0]+1 Xinc = float(X)/(1.0*nFrames) Yinc = float(Y)/(1.0*nFrames) Zinc = float(Z)/(1.0*nFrames) i=frameRange[0] j=1 while i<=frameRange[1]: mv.movie.append((i, "cmd.turn('x',%f);cmd.turn('y',%f);cmd.turn('z',%f);cmd.turn('y',%f);cmd.tur n('x',%f)" % (-1.0*(j-1)*Xinc, -1.0*(j-1)*Yinc, Zinc, j*Yinc, j*Xinc))) i+=1 j+=1 def mvXrotYtoZturn(frames="1",X=0,YtoZ=0): """ Actually this is another attempt to get these complex rotations going a bit smoothly, now trying to keep the thing rotating, while turning around the z-axis. """ frameRange = parseFrames(frames) nFrames = frameRange[1]-frameRange[0]+1 Xinc = float(X)/(1.0*nFrames) YtoZinc = float(YtoZ)/(1.0*nFrames) i=frameRange[0] j=1 while i<=frameRange[1]: mv.movie.append((i, "cmd.turn('x',%f);cmd.turn('y',%f);cmd.turn('z',%f);cmd.turn('y',%f);cmd.tur n('x',%f)" % (-1.0*(j-1)*Xinc, -1.0*(j-1)*Yinc, Zinc, j*Yinc, j*Xinc))) i+=1 j+=1 def mvMove(frames="1",axis="x",totalDistance="0"): """ mvMove(frames,axis,totalDistance) - moves the environment over the given frame range, the moved distance summing up to the totalDistance given. """ frameRange = parseFrames(frames) nFrames = frameRange[1]-frameRange[0]+1 distanceIncrement = float(totalDistance)/(1.0*nFrames) i=frameRange[0] while i<=frameRange[1]: mv.movie.append((i,"move %s,%f"%(axis,distanceIncrement))) i+=1 def mvCmd(frames="1",command=""): """ mvCmd(frames,command) - executes a command in all frames specified. """ frameRange = parseFrames(frames) nFrames = frameRange[1]-frameRange[0]+1 i=frameRange[0] while i<=frameRange[1]: mv.movie.append((i,command)) i+=1 def mvSet(frames="1",variable="",startValue="0.0",endValue="1.0",selection=''): """ mvSet(frames,variable,startValue,endValue,selection) - lets a variable go through a gradient in the specified frame range. Great for fading effects! """ frameRange = parseFrames(frames) nFrames = frameRange[1]-frameRange[0]+1 stV=float(startValue) endV=float(endValue) increment = (endV-stV)/(1.0*nFrames) i=frameRange[0] j=0 while i<=frameRange[1]: mv.movie.append((i,"set %s,%f,%s"%(variable,stV+j*increment,selection))) j+=1 i+=1 def mvSinrot(frames="1",axis="z",totalAngle=360): """ mvSinrot(frames,axis,totalAngle) - rotates the environment over the given frame range, the angles summing up to the totalAngle given. The incremental steps will be calculated with the sinus function to make the rotation smoother. """ frameRange = parseFrames(frames) nFrames = frameRange[1]-frameRange[0]+1 angle = float(totalAngle) arcIncrement=pi/(1.0*nFrames) i=frameRange[0] j=1 prev=1.0 while i<=frameRange[1]: arc=cos(j*arcIncrement) # print arc," * ",abs(arc-prev)*0.5 angleIncrement=angle*abs(arc-prev)*0.5 mv.movie.append((i,"turn %s,%f"%(axis,angleIncrement))) prev=arc j+=1 i+=1 def mvSinmove(frames="1",axis="x",totalDistance="0"): """ mvSinmove(frames,axis,totalDistance) - moves the environment over the given frame range, the moved distance summing up to the totalDistance given. The incremental steps will be calculated with the sinus function to make the movement smoother. """ frameRange = parseFrames(frames) nFrames = frameRange[1]-frameRange[0]+1 dist = float(totalDistance) arcIncrement=pi/(1.0*nFrames) i=frameRange[0] j=1 prev=1.0 while i<=frameRange[1]: arc=cos(j*arcIncrement) # print arc," * ",abs(arc-prev)*0.5 distanceIncrement=dist*abs(arc-prev)*0.5 mv.movie.append((i,"move %s,%f"%(axis,distanceIncrement))) prev=arc j+=1 i+=1 def mvSinset(frames="1",variable="",startValue="0.0",endValue="1.0",selection='' ): """ mvSet(frames,variable,startValue,endValue) - lets a variable go through a gradient in the specified frame range. Great for fading effects! The incremental steps will be calculated with the sinus function to make the movement smoother. """ frameRange = parseFrames(frames) nFrames = frameRange[1]-frameRange[0]+1 arcIncrement=pi/(1.0*nFrames) stV=float(startValue) endV=float(endValue) i=frameRange[0] j=1 prev=1.0 sum=0.0 while i<=frameRange[1]: arc=cos(j*arcIncrement) increment=(endV-stV)*abs(arc-prev)*0.5 sum+=increment mv.movie.append((i,"set %s,%f,%s"%(variable,stV+sum,selection))) prev=arc j+=1 i+=1 def mvClip(frames="1",mode="near",distance="0"): frameRange = parseFrames(frames) nFrames = frameRange[1]-frameRange[0]+1 dist=float(distance) increment = (dist)/(1.0*nFrames) i=frameRange[0] j=0 while i<=frameRange[1]: mv.movie.append((i,"clip %s,%f"%(mode,increment))) i+=1 def mvGradCol(frames="1",selection="",tmpc="",startR="1.0",startG="1.0",startB=" 1.0",endR="0.0",endG="0.0",endB="0.0"): """ mvGradCol(frames,selection,tmpc,R1,G1,B1,R2,G2,B2) - changes colour gradually from (R1,G1,B1) to (R1,G1,B1) through the specified frame range and on the specified selection. """ frameRange = parseFrames(frames) nFrames = frameRange[1]-frameRange[0]+1 stRV=float(startR) endRV=float(endR) incR = (endRV-stRV)/(1.0*nFrames) stGV=float(startG) endGV=float(endG) incG = (endGV-stGV)/(1.0*nFrames) stBV=float(startB) endBV=float(endB) incB = (endBV-stBV)/(1.0*nFrames) i=frameRange[0] j=0 while i<=frameRange[1]: mv.movie.append((i,"set_color %s,[ %f,%f,%f ]"%(tmpc,stRV+j*incR,stGV+j*incG,stBV+j*incB))) mv.movie.append((i,"color %s, %s"%(tmpc,selection))) j+=1 i+=1 def mvBG(frames='1',R1=0.0,B1=0.0,G1=0.0,R2=0.0,G2=0.0,B2=0.0,): """ mvBG lets the background colour fade from RGB1 to RGB2. Isn't that a nice feature ;) """ frameRange = parseFrames(frames) nFrames = frameRange[1]-frameRange[0]+1 R1=float(R1) R2=float(R2) Ri = (R2-R1)/(1.0*nFrames) G1=float(G1) G2=float(G2) Gi = (G2-G1)/(1.0*nFrames) B1=float(B1) B2=float(B2) Bi = (B2-B1)/(1.0*nFrames) i=frameRange[0] j=0 while i<=frameRange[1]: mv.movie.append((i,"cmd.set('bg_rgb',[%f,%f,%f])" %(R1+j*Ri,G1+j*Gi,B1+j*Bi))) j+=1 i+=1 def mvSinBG(frames='1',R1=0.0,B1=0.0,G1=0.0,R2=0.0,G2=0.0,B2=0.0,): """ This does essentially the same as the one above, but uses the gradual transition as the other mvSin* commands. Even better! :) (Why would you ever need this?) """ frameRange = parseFrames(frames) nFrames = frameRange[1]-frameRange[0]+1 arcIncrement=pi/(1.0*nFrames) R1=float(R1) R2=float(R2) Ri = (R2-R1)/(1.0*nFrames) G1=float(G1) G2=float(G2) Gi = (G2-G1)/(1.0*nFrames) B1=float(B1) B2=float(B2) Bi = (B2-B1)/(1.0*nFrames) i=frameRange[0] j=1 prev=1.0 Rt=0.0 Gt=0.0 Bt=0.0 while i<=frameRange[1]: arc=cos(j*arcIncrement) Ri=(R2-R1)*abs(arc-prev)*0.5 Rt+=Ri Gi=(G2-G1)*abs(arc-prev)*0.5 Gt+=Gi Bi=(B2-B1)*abs(arc-prev)*0.5 Bt+=Bi mv.movie.append((i,"cmd.set('bg_rgb',[%f,%f,%f])"%(Rt,Gt,Bt))) prev=arc j+=1 i+=1 def mvMorph(frames='1',source='',target='',filename='morph_script.pml'): """ Atoms from source selection one will be _alter_ed to be on positions from target atoms at end time. This is very time consuming! It writes and uses an external script which is read every frame, since the list of commands will be very long usually. """ frameRange = parseFrames(frames) nFrames = 1.0*(frameRange[1]-frameRange[0]+1) sourceobj = cmd.get_model(source) targetobj = cmd.get_model(target) natoms = len(sourceobj.atom) if not len(targetobj.atom) == natoms: print "Something wrong, target selection is of different size as source!" return if filename[-4:] == '.pml': filename = filename[:-4] filename = filename + repr(frameRange[0]) + '.pml' fout = open(filename, 'a') for i in range(natoms): r1 = sourceobj.atom[i].coord r2 = targetobj.atom[i].coord if r1 != r2: shift = [ (r2[0]-r1[0])/(nFrames - 1), (r2[1]-r1[1])/(nFrames - 1), (r2[2]-r1[2])/(nFrames - 1) ] atomid = 'id %d' % sourceobj.atom[i].id if shift[0]: fout.write('alter_state 1,%s,x=x+ %f\n' % (source + ' and ' + atomid, shift[0])) if shift[1]: fout.write('alter_state 1,%s,y=y+ %f\n' % (source + ' and ' + atomid, shift[1])) if shift[2]: fout.write('alter_state 1,%s,z=z+ %f\n' % (source + ' and ' + atomid, shift[2])) i=frameRange[0] j=0 while i<=frameRange[1]: mv.movie.append((i,"cmd.do('@%s')"%filename)) j+=1 i+=1 def mvDel(frames="1",S1="all",S2="all",Rstart=50): frameRange = parseFrames(frames) nFrames = frameRange[1]-frameRange[0]+1 Rstart=float(Rstart) Rinc = (Rstart)/(1.0*nFrames) i=frameRange[0] j=0 while i<=frameRange[1]: mv.movie.append((i,"remove %s and not (%s within %f of %s)"%(S1,S1,Rstart - j*Rinc,S2))) i+=1 j+=1 def mvForward(): """Creates the movie and plays it.""" # find out the frame range nFrames=1 for m in mv.movie: if m[0]>nFrames: nFrames=m[0] # Specify movie length print "creating movie with %i frames."%(nFrames) cmd.mset("1 x%i"%(nFrames)) # create empty frame-2do-lists do=["zero frame is unused"] for i in range(nFrames): do.append("") # push all movie commands to the 2do-list for m in mv.movie: do[m[0]]+=m[1]+";" # now let action happen in the frames i=1 while i<=nFrames: cmd.mdo(i,do[i]) i+=1 # start the movie cmd.mplay() def mvBackward(): # not implemented yet return # ---------------------------------------------- def fwd(amount): for i in range(int(amount)): mvMovie.append("move z,5") backmovie.append("move z,-5") # ----------------------------------------------------------------- # define a set of new PyMOL commands for creation of movies # this part will be executed first upon the 'run movie.py' command from within PyMOL. cmd.extend('movie',mvForward) # cmd.extend('movie_back',mvBackward) cmd.extend('mvClear',mvClear) cmd.extend('mvRot',mvRot) cmd.extend('mvMove',mvMove) cmd.extend('mvCmd',mvCmd) cmd.extend('mvSet',mvSet) cmd.extend('mvSinrot',mvSinrot) cmd.extend('mvSinmove',mvSinmove) cmd.extend('mvSinset',mvSinset) # TAW additions cmd.extend('mvGradCol',mvGradCol) cmd.extend('mvClip',mvClip) cmd.extend('mvBG',mvBG) cmd.extend('mvSinBG',mvSinBG) cmd.extend('mvMorph',mvMorph) cmd.extend('mvCxRot',mvCxRot) cmd.extend('mvDel',mvDel)