# CM; pymol_movies.py version 1.0; June 2003 # import the basic Python glob module and PyMOL commands, etc.: import sys; from glob import glob; from pymol import cmd; """ AUTHOR Cameron Mura (cmura@ucsd.edu) USAGE Unix/Linux systems: either (a) load this file ("pymol_movies.py") into your PyMOL script by including the line "run /path-to-this-file/pymol_movies.py" near top of your PyMOL script file (.pml) or (b) enter the above "run ..." command at the prompt of an interactive PyMOL session Other systems: This module has not been tested on MacOS X or Windows-PC systems. However, as long as the appropriate Python and PyMOL modules are in place, it should work fine. For example, on a Windows PC simply save this file in the same folder in which you're working and load it into PyMOL as described above. Of these functions, write_movie() is most likely to suffer from incompatability on non-Unix platforms. Overview: This module defines the following three functions which may be useful in creating animations in PyMOL: spin_movie() -- Spins an already-loaded molecular object 180-degrees about the y-, x-, and z-axes (in that order). Works well when the protein is rendered as a ribbon cartoon. The animation then switches the view to CPK spheres for two short intervals (during rotation about the y- and x-axes). frames_movie() -- Animate through several PDB files. Given a basename for a series of PDB files (e.g., "1G5V" for the files 1G5V_4.pdb, 1G5V_5.pdb, ..., 1G5V_123.pdb), this function loads the files individually, and also loads them into a PyMOL movie object (named "movie"). As implemented here, each frame of the movie is rendered as a C-alpha trace, rainbow-colored from N- to C-terminus...(Easy to change this). write_movie() -- This function actually writes-out the movie that was created by "spin_movie()", "frames_movie()", or manually by the user. As far as this script goes, it's best not to have >1 movie loaded in PyMOL at a given time (otherwise may get confused). User specifies: (i) movie basename (used for individual .png files written by PyMOL into the local ./png/ directory), (ii) movie format (choices are "gif" or the default "mpg"), and (iii) number of frames to assemble into movie. Syntax (quoted strings in these syntax descriptions means quotes MUST appear in actual usage): * spin_movie("some-selection") -- where 'some-selection' is a valid PyMOL named selection of atoms, such as "all" for every atom that is loaded and currently displayed in PyMOL, "chain_a" if you've already defined a group of atoms named "chain_a", etc. Any valid PyMOL selection string may be used - e.g., "all and (n;c,ca,o,n)" to select all protein backbone atoms. * frames_movie("pdb-files-basename",int1,int2,"delay-string") -- where 'pdb-files-basename' is a quoted string that is the basename for a series of PDB files in the current directory. 'int1' and 'int2' are positive integers that correspond to the minimum and maximum indices of these PDB files, respectively (not necessarily beginning with '1'). In the example above, 'int1' is 4 and 'int2' is 123. 'delay-string' is either "long", "medium", or "short" (default), corresponding to the delay between frames in a movie. Note that this feature isn't really necessary, since most programs for assembling the individual PyMOL-output .png files will be capable of controlling the delay between frames. This is a kinda artificial workaround. * write_movies("movie-basename","movie-format",int1) -- where 'movie-basename' is a quoted string that will be the basename for all the .png files that PyMOL outputs (e.g., crap_1.png, crap_2.png, crap_3.png, etc.), as well as the final movie (which will be named 'movie-basename.gif' or .mpg). 'movie-format' is either "gif" or "mpg" (default), and determines the file format of the final movie (note that animated GIFs can easily become too large). 'int1' is the integer number of frames to assemble into movie. * Any of these functions may be used without parentheses; in such cases do NOT use quotes for the arguments. For example, the following two calls are equivalent: frames_movie("1G5V",4,123,"medium") <==> frames_movie 1G5V,4,123,medium NOTES: (0) The latest version of PyMOL (0.88pre4) can read PDB files containing multiple models (delimited by 'MODEL' and 'ENDMDL' tags), and assemble them into a movie (by default). This works well, e.g. for NMR models, but can't control things like the range and order of states that get assembled into frames (see the "mset_command" in the code below). (1) Use the associated Perl script (split_pdb_models.pl) to split a PDB file containing multiple models into individual files that are sequentially numbered by model number. (2) Suitable defaults have been put in place, and warning messages may be printed out (so keep an eye on the PyMOL text box). In spin_movie() the selection defaults to "all" (i.e. everything that is currently displayed). For frames_movie() the default values for the minimum and maximum indices of the PDB files are 1 and 10, and the default delay-string is "short". For write_movies(), the default movie-basename is "mymovie" and the default movie-format is "mpg". (3) The "ray_trace_frames" and "antialias" settings are off ('0') by default, because they lead to large-sized movies (easily >10MB). If you want ray-tracing or antialiasing of each movie frame, set these to on ('1') in lines ~225-230 below. (4) Because of mclear() function calls, it's probably OK (but not recommended) to work with multiple movies at once. (5) Use of write_movie() function to write MPG and GIF formats requires that ImageMagick's "convert" program (http://www.imagemagick.org/) be included in your path, and the MPG-writing option also requires the MPEG packages "makempeg" utility (available from http://rpmfind.maine.edu/RPM/powertools/7.1/i386/MPEG-1.2.2-12.i386.html). On Windows PCs, a safer bet may be to simply use the spin_movie() and frames_movie() functions to create movies, manually write out .png files with PyMOL's "mpng" command, and then use your favorite image-processing software (Adobe Premiere, Logipole's Konvertor, etc.) to assemble .png frames into a movie. CURRENT SHORTCOMINGS/planned improvements(?): * Allow the spin_movie() function to operate with respect to the universe of all loaded PyMOL molecular objects, rather than just what is displayed at the moment the function is called. For example, say you have a molecule "sample" with four atom selections defined from it (chain_a, chain_b, chain_c, chain_d). If only chain_b and chain_c are displayed, then spin_movie("all") will not actually display everything (i.e. sample, chain_a, chain_b, chain_c, and chain_d) and then animate it, but rather it will display and animate everything that is currently displayed. So, if you have only chain_c and chain_d showing, but want to make an animation of chain_a and chain_b alone, then spin_movie("chain_a | chain_b") will not work, as currently implemented. This is all due to the fact that PyMOL's "show" and "hide" functions take two arguments -- (i) the type of representation (ribbon, spheres, etc.) to show/ hide and (ii) the atom selection on which to operate. What I need is a function that returns the exact state of display (lines, spheres, ribbons, whatever) for atom selection 'x'..(e.g. what was the last state in which chain_a | chain_b were displayed? etc.) * Add functionality to spin_movie(), allowing user-input control over such parameters as the sequence of axes to rotate about, extent of rotation, zooming, etc...Clean-up the write_movie() function...particularly in terms of hacking PyMOL to make it write only a segment of frames (not ALL of them) via an 'mpng'-like command. """ def spin_movie(selection=""): if selection == "": print "*** NO SELECTION WAS DEFINED -- using everything currently displayed!\n"; selection = "all"; print "\n***********************************************"; print "SELECTION that will be animated: ",selection; print "***********************************************\n"; m = cmd.get_model(selection); if len(m.atom) == 0: print "*** WARNING: NO ATOMS in the selection \"%s\"!! Proceeding anyway...\n" % (selection); else: print "Number of atoms in selection \"%s\":\t%i" % (selection,len(m.atom)); stuff_to_hide = "all and not ("+selection+")"; cmd.hide("everything",stuff_to_hide); cmd.mclear(); cmd.mset("1 x300"); # assumption here is that /usr/lib/python2.2/site-packages/pymol/modules/pymol/movie.py or a similar module exists... # otherwise explicity do a "run /path-to-movie.py/movie.py" and issue commands as tdroll(...) etc... movie.tdroll(1,0,180,0,2); movie.tdroll(100,180,0,0,2); movie.tdroll(200,0,0,180,2); cmd.mdo(60,"show spheres, ("+selection+")"); cmd.mdo(85,"hide spheres, ("+selection+")"); cmd.mdo(130,"show spheres, ("+selection+")"); cmd.mdo(160,"hide spheres, ("+selection+")"); # stop movie at last frame (300): cmd.mdo(300,"cmd.mstop(); frame_count = cmd.get_frame(); print \"frame_count = \",frame_count;"); # activate this if you want movie to stop at last frame instead of looping forever.. #cmd.mdo(300,"cmd.rewind();"); # activate this to do rewind at last frame #play the movie (not necessarily as it will be written out, if you set viewport, ray_trace_frames, and antialias variables below): cmd.mplay(); ############################################################################################################################################## ########################### "frames_movie() function is another way to make a movie: this time going through multiple models ################# def frames_movie(pdb_files_basename="",min_index=1,max_index=10,delay_type="short"): if pdb_files_basename == "": print "\n*** ERROR: must input basename for series of PDB files (e.g., \"model_\")!!! ***\n"; if delay_type == "long": frame_repeat = 6; elif delay_type == "medium": frame_repeat = 3; else: # default is to assume "short" intervals between PDB models...use short delay between them. frame_repeat = 1; min_index=int(min_index); max_index=int(max_index); num_files = max_index - min_index + 1; print "# of files you want to animate through: %i\n" %(num_files); counter = min_index; mset_command = ""; while counter <= max_index: current_file = pdb_files_basename + "_" + str(counter) + ".pdb"; frame_index = counter - min_index + 1; # to keep track of frames, in case min_index doesn't start at 1... print "Loading file: ",current_file; cmd.load(current_file,pdb_files_basename + "_" + str(counter)); cmd.load(current_file,"movie"); # also load the files into a movie object called "movie" mset_command = mset_command + str(frame_index) +" x"+str(frame_repeat)+" "; #print "mset_command = ",mset_command; # this was for debugging... ''' PUT WHATEVER PYMOL STUFF YOU WANT IN HERE to systematically alter the protein object in each frame of the movie. The example that's commented-out in the next several lines first hides everything, then shows a C-alpha trace for each frame of the movie... ''' cmd.hide("everything",pdb_files_basename + "_" + str(counter)); #cmd.select(str(counter)+"_ca","n;ca"); # if you want to explicitly select C-alphas and make them an object... #cmd.show("ribbon",str(counter)+"_ca"); # ... #cmd.show("ribbon",pdb_files_basename + "_" + str(counter) + " and n;ca"); # this can replace above line if don't explicitly #cmd.set("ribbon_trace",1); # select C-alpha object 2 lines above #cmd.set("ribbon_sampling",1); #cmd.deselect(); counter = counter + 1; cmd.mclear(); # flush movie frame cache cmd.mset(mset_command); cmd.hide("everything","movie"); cmd.set("ribbon_trace",1); cmd.set("ribbon_sampling",1); cmd.set("ribbon_width",3); cmd.show("ribbon","movie and n;ca"); # do it this way since we opted not to explicitly select C-alphas up above... util.rainbow("movie"); final_frame_number = num_files * frame_repeat #cmd.mdo(final_frame_number,"cmd.mstop();"); # activate this if you want movie to stop at last frame instead of looping forever.. frame_count = final_frame_number; print "frame_count = ",frame_count; #cmd.mdo(final_frame_number,"cmd.rewind();"); # activate this to do rewind at last frame cmd.mplay(); #################### Here's the write_movie function, which actually writes out the movie frames, assembles them, etc... ################### def write_movie(movie_basename="",movie_format="",frame_count=10): frame_count=int(frame_count); if frame_count < 1: print "*** NO FRAMECOUNT specified-- will use default value of 10!\n"; frame_count=10; if movie_basename == "": print "*** NO BASENAME was defined -- will use default value of \"mymovie\"!\n"; movie_basename = "mymovie"; if movie_format == "": print "*** NO MOVIE FORMAT WAS SPECIFIED -- will use MPG format by default!\n"; movie_format = "mpg"; cmd.viewport(320,240); cmd.mclear(); # turn off ray-tracing or anti-aliasing features for MUCH smaller movies: cmd.set("ray_trace_frames",0); cmd.set("cache_frame",0); cmd.set("antialias",0); print "\n******************************************************"; print "Here's a PREVIEW of movie that will be written out..."; print "******************************************************\n"; cmd.mplay(); # make output directory if doesn't already exist, write out frames: if os.path.exists("png"): os.system("rm png/"+movie_basename+"_*.png") else: os.mkdir("png"); cmd.mpng("png/"+movie_basename); # First branch of 'if' converts .png --> .gif --> .mpg , using "convert" and "makempeg" programs... # Second branch converts .png --> .gif (animated GIF) using "convert" program: if movie_format == "mpg": if not os.path.exists("gif"): os.mkdir("gif") for i in glob("png/"+movie_basename+"*.png"): os.system("convert "+i+" "+re.sub("png","gif",re.sub("_",".",i))) os.system("/bin/sh -c \"cd ./gif; makempeg -fs 1 -fe "+str(frame_count)+" -base "+movie_basename+" -ext gif\""); os.system("mv gif/"+movie_basename+".mpg ."); elif movie_format == "gif": os.system("convert -loop 100 -delay 2 -verbose png/"+movie_basename+"_*.png "+movie_basename+".gif"); # restore original viewport size: cmd.viewport(640,480); # bind these three external functions as commands in the PyMOL session...so can do function-calling without parentheses... cmd.extend("spin_movie",spin_movie) cmd.extend("frames_movie",frames_movie) cmd.extend("write_movie",write_movie)