movie.py (pymol-v1.8.6.0.tar.bz2) | : | movie.py (pymol-v2.1.0.tar.bz2) | ||
---|---|---|---|---|
skipping to change at line 17 | skipping to change at line 17 | |||
#G* Please see the accompanying LICENSE file for further information. | #G* Please see the accompanying LICENSE file for further information. | |||
#H* ------------------------------------------------------------------- | #H* ------------------------------------------------------------------- | |||
#I* Additional authors of this source file include: | #I* Additional authors of this source file include: | |||
#-* Peter Haebel, Byron DeLaBarre | #-* Peter Haebel, Byron DeLaBarre | |||
#-* | #-* | |||
#-* | #-* | |||
#Z* ------------------------------------------------------------------- | #Z* ------------------------------------------------------------------- | |||
from __future__ import print_function | from __future__ import print_function | |||
cmd = __import__("sys").modules["pymol.cmd"] | import sys | |||
import glob | cmd = sys.modules["pymol.cmd"] | |||
import math | import math | |||
import os | import os | |||
import glob | import glob | |||
import threading | import threading | |||
import time | import time | |||
def get_movie_fps(_self): | def get_movie_fps(_self): | |||
r = _self.get_setting_float('movie_fps') | r = _self.get_setting_float('movie_fps') | |||
if r <= 0: | if r <= 0: | |||
return 30 | return 30 | |||
skipping to change at line 273 | skipping to change at line 273 | |||
frame = 1 | frame = 1 | |||
for cycle in range(cycles): | for cycle in range(cycles): | |||
for cnt in range(frames_per_cycle): | for cnt in range(frames_per_cycle): | |||
_self.turn(axis,deg) | _self.turn(axis,deg) | |||
_self.mview('store',frame,freeze=1) | _self.mview('store',frame,freeze=1) | |||
frame = frame + 1 | frame = frame + 1 | |||
# blank frames to be specified by the user | # blank frames to be specified by the user | |||
def add_blank(duration=12.0,start=0,_self=cmd): | def add_blank(duration=12.0,start=0,_self=cmd): | |||
''' | ||||
DESCRIPTION | ||||
Add "blank" time to the movie (without any key frames) | ||||
ARGUMENTS | ||||
duration = float: time to add to movie in seconds {default: 12} | ||||
start = int: start frame (1 = first frame; 0 = end of movie) {default: 0} | ||||
SEE ALSO | ||||
mset | ||||
''' | ||||
cmd = _self | cmd = _self | |||
if not start: | if not start: | |||
start = cmd.get_movie_length()+1 | start = cmd.get_movie_length()+1 | |||
duration = float(duration) | duration = float(duration) | |||
fps = get_movie_fps(_self) | fps = get_movie_fps(_self) | |||
n_frame = int(round(fps * duration)) | n_frame = int(round(fps * duration)) | |||
if n_frame > 0: | if n_frame > 0: | |||
cmd.mset("1 x%d"%n_frame,start) | cmd.mset("1 x%d"%n_frame,start) | |||
cmd.frame(start) | cmd.frame(start) | |||
# new matrix-based camera interpolation routines | # new matrix-based camera interpolation routines | |||
def add_roll(duration=12.0,loop=1,axis='y',start=0,_self=cmd): | def add_roll(duration=12.0,loop=1,axis='y',start=0,_self=cmd): | |||
''' | ||||
DESCRIPTION | ||||
Append a 360 degree camera rotation to the movie, using key frames. | ||||
ARGUMENTS | ||||
duration = float: time to add to movie in seconds {default: 12} | ||||
loop = 0/1: ??? | ||||
axis = x, y or z: rotation axis in camera space {default: y} | ||||
start = int: start frame (1 = first frame; 0 = end of movie) {default: 0} | ||||
''' | ||||
cmd = _self | cmd = _self | |||
if not start: | if not start: | |||
start = cmd.get_movie_length()+1 | start = cmd.get_movie_length()+1 | |||
duration = float(duration) | duration = float(duration) | |||
fps = get_movie_fps(_self) | fps = get_movie_fps(_self) | |||
n_frame = int(round(fps * duration)) | n_frame = int(round(fps * duration)) | |||
if n_frame > 0: | if n_frame > 0: | |||
cmd.mset("1 x%d"%n_frame,start,freeze=1) | cmd.mset("1 x%d"%n_frame,start,freeze=1) | |||
cmd.mview("store",start,power=1,freeze=1) | cmd.mview("store",start,power=1,freeze=1) | |||
cmd.turn(axis,120) | cmd.turn(axis,120) | |||
skipping to change at line 316 | skipping to change at line 346 | |||
adjustment = 360.0/n_frame | adjustment = 360.0/n_frame | |||
cmd.turn(axis,120 - adjustment) | cmd.turn(axis,120 - adjustment) | |||
cmd.mview("store",start+n_frame-1,power=1,freeze=1) | cmd.mview("store",start+n_frame-1,power=1,freeze=1) | |||
cmd.mview("interpolate") | cmd.mview("interpolate") | |||
cmd.turn(axis,adjustment) | cmd.turn(axis,adjustment) | |||
else: | else: | |||
cmd.turn(axis,120) | cmd.turn(axis,120) | |||
cmd.mview("store",start+n_frame-1,power=1,freeze=1) | cmd.mview("store",start+n_frame-1,power=1,freeze=1) | |||
cmd.mview("interpolate") | cmd.mview("interpolate") | |||
cmd.frame(start) | cmd.frame(start) | |||
# PYMOL-2881 | ||||
if cmd.get_setting_int('movie_auto_interpolate'): | ||||
cmd.mview("reinterpolate") | ||||
def add_rock(duration=8.0,angle=30.0,loop=1,axis='y',start=0,_self=cmd): | def add_rock(duration=8.0,angle=30.0,loop=1,axis='y',start=0,_self=cmd): | |||
''' | ||||
DESCRIPTION | ||||
Append a rocking camera motion to the movie, using key frames. | ||||
ARGUMENTS | ||||
duration = float: time to add to movie in seconds {default: 8} | ||||
angle = float: degrees {default: 30} | ||||
loop = 0/1: ??? | ||||
axis = x, y or z: rotation axis in camera space {default: y} | ||||
start = int: start frame (1 = first frame; 0 = end of movie) {default: 0} | ||||
''' | ||||
cmd = _self | cmd = _self | |||
if not start: | if not start: | |||
start = cmd.get_movie_length()+1 | start = cmd.get_movie_length()+1 | |||
duration = float(duration) | duration = float(duration) | |||
angle=float(angle) | angle=float(angle) | |||
loop=int(loop) | loop=int(loop) | |||
fps = get_movie_fps(_self) | fps = get_movie_fps(_self) | |||
n_frame = int(round(fps * duration)) | n_frame = int(round(fps * duration)) | |||
if n_frame > 0: | if n_frame > 0: | |||
cmd.mset("1 x%d"%n_frame,start,freeze=1) | cmd.mset("1 x%d"%n_frame,start,freeze=1) | |||
skipping to change at line 359 | skipping to change at line 409 | |||
cmd.mview("store",start, state=1, freeze=1) | cmd.mview("store",start, state=1, freeze=1) | |||
cmd.mview("store",start + (n_frame * pause) / duration, state=1, freeze= 1) | cmd.mview("store",start + (n_frame * pause) / duration, state=1, freeze= 1) | |||
cmd.mview("store",start + n_state * factor + (n_frame * pause) / duratio n - 1, state=n_state, freeze=1) | cmd.mview("store",start + n_state * factor + (n_frame * pause) / duratio n - 1, state=n_state, freeze=1) | |||
cmd.mview("store",start + n_state * factor + (2 * n_frame * pause) / dur ation, state=n_state, freeze=1) | cmd.mview("store",start + n_state * factor + (2 * n_frame * pause) / dur ation, state=n_state, freeze=1) | |||
cmd.mview("store",start + n_frame-1, state=1, freeze=1) | cmd.mview("store",start + n_frame-1, state=1, freeze=1) | |||
if loop and (start == 1): | if loop and (start == 1): | |||
cmd.mview("interpolate",wrap=1) | cmd.mview("interpolate",wrap=1) | |||
else: | else: | |||
cmd.mview("interpolate") | cmd.mview("interpolate") | |||
cmd.frame(start) | cmd.frame(start) | |||
# PYMOL-2881 | ||||
if cmd.get_setting_int('movie_auto_interpolate'): | ||||
cmd.mview("reinterpolate") | ||||
def add_state_loop(factor=1,pause=2.0,first=-1,last=-1,loop=1,start=0,_self=cmd) : | def add_state_loop(factor=1,pause=2.0,first=-1,last=-1,loop=1,start=0,_self=cmd) : | |||
cmd = _self | cmd = _self | |||
if not start: | if not start: | |||
start = cmd.get_movie_length() + 1 | start = cmd.get_movie_length() + 1 | |||
loop = int(loop) | loop = int(loop) | |||
fps = get_movie_fps(_self) | fps = get_movie_fps(_self) | |||
n_state = cmd.count_states() | n_state = cmd.count_states() | |||
duration = (pause) + (factor * n_state) / fps | duration = (pause) + (factor * n_state) / fps | |||
n_frame = int(round(fps * duration)) | n_frame = int(round(fps * duration)) | |||
skipping to change at line 380 | skipping to change at line 433 | |||
cmd.mset("1 x%d"%n_frame, start, freeze=1) | cmd.mset("1 x%d"%n_frame, start, freeze=1) | |||
cmd.mview("store",start, state=1, freeze=1) | cmd.mview("store",start, state=1, freeze=1) | |||
cmd.mview("store",start + (n_frame * pause * 0.5) / duration, state=1, f reeze=1) | cmd.mview("store",start + (n_frame * pause * 0.5) / duration, state=1, f reeze=1) | |||
cmd.mview("store",start + n_state * factor + (n_frame * pause * 0.5) / d uration, state=n_state, freeze=1) | cmd.mview("store",start + n_state * factor + (n_frame * pause * 0.5) / d uration, state=n_state, freeze=1) | |||
cmd.mview("store",start + n_frame-1, state=n_state, freeze=1) | cmd.mview("store",start + n_frame-1, state=n_state, freeze=1) | |||
if loop and (start == 1): | if loop and (start == 1): | |||
cmd.mview("interpolate",wrap=1) | cmd.mview("interpolate",wrap=1) | |||
else: | else: | |||
cmd.mview("interpolate") | cmd.mview("interpolate") | |||
cmd.frame(start) | cmd.frame(start) | |||
# PYMOL-2881 | ||||
if cmd.get_setting_int('movie_auto_interpolate'): | ||||
cmd.mview("reinterpolate") | ||||
def add_nutate(duration=8.0, angle=30.0, spiral=0, loop=1, | def add_nutate(duration=8.0, angle=30.0, spiral=0, loop=1, | |||
offset=0, phase=0, shift=math.pi/2.0, start=0, | offset=0, phase=0, shift=math.pi/2.0, start=0, | |||
_self=cmd): | _self=cmd): | |||
''' | ||||
DESCRIPTION | ||||
Append a nutating camera motion to the movie, using key frames. | ||||
ARGUMENTS | ||||
duration = float: time to add to movie in seconds {default: 8} | ||||
angle = float: degrees {default: 30} | ||||
spiral = -1/0/1: If zero, do a circular motion, otherwise do a spiral | ||||
motion starting at the center (clockwise if 1, ccw if -1) {default: 0} | ||||
loop: (unused) | ||||
offset: (unused) | ||||
phase = float: phase offset in degrees {default: 0} | ||||
shift = float: x to y offset in radians {default: pi/2} | ||||
start = int: start frame (1 = first frame; 0 = end of movie) {default: 0} | ||||
''' | ||||
cmd = _self | cmd = _self | |||
if not start: | if not start: | |||
start = cmd.get_movie_length()+1 | start = cmd.get_movie_length()+1 | |||
duration = float(duration) | duration = float(duration) | |||
angle = float(angle) | angle = float(angle) | |||
spiral = int(spiral) | spiral = int(spiral) | |||
loop = int(loop) | loop = int(loop) | |||
fps = get_movie_fps(_self) | fps = get_movie_fps(_self) | |||
n_frame = int(round(fps * duration)) | n_frame = int(round(fps * duration)) | |||
if n_frame > 0: | if n_frame > 0: | |||
skipping to change at line 410 | skipping to change at line 490 | |||
else: | else: | |||
sp_angle = angle | sp_angle = angle | |||
ang_cur = math.pi*phase/180.0 + (2*math.pi*index)/n_frame | ang_cur = math.pi*phase/180.0 + (2*math.pi*index)/n_frame | |||
x_rot = sp_angle * math.sin(ang_cur)/2 | x_rot = sp_angle * math.sin(ang_cur)/2 | |||
y_rot = sp_angle * math.sin(ang_cur+shift)/2 | y_rot = sp_angle * math.sin(ang_cur+shift)/2 | |||
cmd.turn('x',x_rot) | cmd.turn('x',x_rot) | |||
cmd.turn('y',y_rot) | cmd.turn('y',y_rot) | |||
cmd.mview('store',start+index,freeze=1) | cmd.mview('store',start+index,freeze=1) | |||
cmd.turn('y',-y_rot) | cmd.turn('y',-y_rot) | |||
cmd.turn('x',-x_rot) | cmd.turn('x',-x_rot) | |||
# PYMOL-2881 | ||||
if cmd.get_setting_int('movie_auto_interpolate'): | ||||
cmd.mview("reinterpolate") | ||||
def _rock(mode,axis,first,last,period,pause,_self=cmd): | def _rock(mode,axis,first,last,period,pause,_self=cmd): | |||
cmd = _self | cmd = _self | |||
n_frame = last - first + 1 | n_frame = last - first + 1 | |||
angle = cmd.get_setting_float('sweep_angle') | angle = cmd.get_setting_float('sweep_angle') | |||
if (period * 1.5) < pause: | if (period * 1.5) < pause: | |||
n_cyc = int(round(pause / period)) | n_cyc = int(round(pause / period)) | |||
frame_list = [] | frame_list = [] | |||
for cyc in range(n_cyc): | for cyc in range(n_cyc): | |||
frame_list.extend( [(first + ((1+4*cyc)*n_frame)/(4*n_cyc)), | frame_list.extend( [(first + ((1+4*cyc)*n_frame)/(4*n_cyc)), | |||
skipping to change at line 480 | skipping to change at line 563 | |||
n_cyc = int(round(pause / period)) | n_cyc = int(round(pause / period)) | |||
frame_list = [] | frame_list = [] | |||
for cyc in range(n_cyc): | for cyc in range(n_cyc): | |||
frame_list.append( [(first + ((cyc)*n_frame)/(n_cyc)), | frame_list.append( [(first + ((cyc)*n_frame)/(n_cyc)), | |||
(first + ((cyc+1)*n_frame)/(n_cyc))] ) | (first + ((cyc+1)*n_frame)/(n_cyc))] ) | |||
else: | else: | |||
frame_list = [ [first, first+n_frame] ] | frame_list = [ [first, first+n_frame] ] | |||
direction = 0 | direction = 0 | |||
spiral = 1 | spiral = 1 | |||
for frame in frame_list: | for frame in frame_list: | |||
_nutate_sub(frame[0], frame[1], angle, spiral, _self=cmd) | _nutate_sub(frame[0], frame[1], angle, spiral, _self=_self) | |||
spiral = 0 | spiral = 0 | |||
def add_scenes(names=None, pause=8.0, cut=0.0, loop=1, | def add_scenes(names=None, pause=8.0, cut=0.0, loop=1, | |||
rock=-1, period=8.0, animate=-1, start=0, | rock=-1, period=8.0, animate=-1, start=0, | |||
_self=cmd): | _self=cmd): | |||
''' | ||||
DESCRIPTION | ||||
Append a sequence of scenes to the movie, with camera animation | ||||
(rock/nutate) at each scene. | ||||
ARGUMENTS | ||||
names = str: list of scenes names {default: all scenes} | ||||
pause = float: display time per scene in seconds {default: 8} | ||||
cut = float 0.0-1.0: scene switch moment (0.0: beginning of transition, | ||||
1.0: end of transition) {default: 0.0} | ||||
loop = 0/1: end of movie interpolates back to first frame {default: 1} | ||||
rock = int: (sweep_mode + 1): 2=x-axis-rock, 3=y-axis-rock, 4=nutate | ||||
{default: -1, use sweep_mode setting} | ||||
period = float: rock/nutate time (e.g. "period=2, pause=6" will rock 3 | ||||
times per scene) {default: 8} | ||||
animate = float: scene transition time in seconds | ||||
{default: -1, use scene_animation_duration setting} | ||||
start = int: start frame (1 = first frame; 0 = end of movie) {default: 0} | ||||
''' | ||||
cmd = _self | cmd = _self | |||
if not start: | if not start: | |||
start = cmd.get_movie_length()+1 | start = cmd.get_movie_length()+1 | |||
animate = float(animate) | animate = float(animate) | |||
pause = float(pause) | pause = float(pause) | |||
period = float(period) | period = float(period) | |||
if period>pause: | if period>pause: | |||
period = pause | period = pause | |||
rock = int(rock) | rock = int(rock) | |||
if animate<0: | if animate<0: | |||
skipping to change at line 580 | skipping to change at line 691 | |||
break | break | |||
print(" produce: %d bytes written..."%size) | print(" produce: %d bytes written..."%size) | |||
else: | else: | |||
tries = tries - 1 | tries = tries - 1 | |||
if tries < 0: | if tries < 0: | |||
break | break | |||
time.sleep(2) | time.sleep(2) | |||
if done_event.isSet(): | if done_event.isSet(): | |||
break | break | |||
def _encode(filename,mode,first,last,preserve, | def _encode(filename,first,last,preserve, | |||
encoder,tmp_path,prefix,quality,quiet,_self=cmd): | encoder,tmp_path,prefix,img_ext,quality,quiet,_self=cmd): | |||
import os | import os | |||
tries = 10 | tries = 10 | |||
while 1: # loop until all of the files have been created... | while 1: # loop until all of the files have been created... | |||
done = 1 | done = 1 | |||
# check for the required output files | # check for the required output files | |||
for index in range(first,last+1): | for index in range(first,last+1): | |||
path = os.path.join(tmp_path,prefix+"%04d.ppm"%index) | path = os.path.join(tmp_path, "%s%04d%s" % (prefix, index, img_ext)) | |||
if not os.path.exists(path): | if not os.path.exists(path): | |||
done = 0 | done = 0 | |||
break; | break; | |||
if done: | if done: | |||
break | break | |||
elif _self.get_modal_draw(): # keep looping so long as we're rendering.. . | elif _self.get_modal_draw(): # keep looping so long as we're rendering.. . | |||
tries = 10 | tries = 10 | |||
else: | else: | |||
tries = tries - 1 | tries = tries - 1 | |||
if tries < 0: | if tries < 0: | |||
done = 0 | done = 0 | |||
break | break | |||
time.sleep(0.25) | time.sleep(0.25) | |||
_self.sync() | _self.sync() | |||
ok = 1 | ok = 1 | |||
result = None | ||||
# reduce chance of passing non-ascii file paths to sub processes | ||||
# by changing directory | ||||
fn_rel = os.path.relpath(filename, tmp_path) | ||||
old_cwd = os.getcwd() | ||||
if done and ok and (encoder == 'mpeg_encode'): | if done and ok and (encoder == 'mpeg_encode'): | |||
try: | try: | |||
from freemol import mpeg_encode | from freemol import mpeg_encode | |||
except: | except: | |||
ok = 0 | ok = 0 | |||
print("produce-error: Unable to import module freemol.mpeg_encode.") | print("produce-error: Unable to import module freemol.mpeg_encode.") | |||
if ok: | if ok: | |||
if not mpeg_encode.validate(): | if not mpeg_encode.validate(): | |||
ok = 0 | ok = 0 | |||
print("produce-error: Unable to validate freemol.mpeg_encode.") | print("produce-error: Unable to validate freemol.mpeg_encode.") | |||
if not ok: | if not ok: | |||
print("produce-error: Unable to create mpeg file.") | print("produce-error: Unable to create mpeg file.") | |||
else: | else: | |||
mpeg_quality = 1+int(((100-quality)*29)/100) # 1 to 30 | mpeg_quality = 1+int(((100-quality)*29)/100) # 1 to 30 | |||
input = mpeg_encode.input(filename,tmp_path, | input = mpeg_encode.input(fn_rel, '.', | |||
prefix,first,last,mpeg_quality); | prefix,first,last,mpeg_quality); | |||
if not quiet: | if not quiet: | |||
print(" produce: creating '%s' (in background)..."%(filename)) | print(" produce: creating '%s' (in background)..."%(filename)) | |||
os.chdir(tmp_path) | ||||
done_event = None | done_event = None | |||
if not quiet: | if not quiet: | |||
done_event = threading.Event() | done_event = threading.Event() | |||
_self.async(_watch, filename, done_event, _self=_self) | _self.async_(_watch, fn_rel, done_event, _self=_self) | |||
try: | try: | |||
result = mpeg_encode.run(input) | result = mpeg_encode.run(input) | |||
finally: | finally: | |||
os.chdir(old_cwd) | ||||
if done_event != None: | if done_event != None: | |||
done_event.set() | done_event.set() | |||
if not quiet: | elif encoder == 'ffmpeg': | |||
fps = get_movie_fps(_self) | ||||
import subprocess | ||||
os.chdir(tmp_path) | ||||
try: | ||||
args = ['ffmpeg', | ||||
'-f', 'image2', | ||||
'-framerate', '%d' % fps, # framerate | ||||
'-i', prefix + '%04d' + img_ext, | ||||
] | ||||
if not fn_rel.endswith('.gif'): | ||||
args += [ | ||||
'-crf', '10' if quality > 90 else '15' if quality > 80 else '20' | ||||
, | ||||
'-pix_fmt', 'yuv420p', # needed for Mac support | ||||
] | ||||
subprocess.check_call(args + [fn_rel]) | ||||
finally: | ||||
os.chdir(old_cwd) | ||||
elif encoder == 'convert': | ||||
import subprocess | ||||
exe = find_exe(encoder) | ||||
try: | ||||
subprocess.check_call([exe, | ||||
os.path.join(tmp_path, prefix) + '*' + img_ext, | ||||
filename]) | ||||
finally: | ||||
pass | ||||
if not quiet: | ||||
if not os.path.exists(filename): | if not os.path.exists(filename): | |||
if result != None: | if result != None: | |||
print(input, result[0], result[1]) | print(input, result[0], result[1]) | |||
print(" produce: compression failed") | print(" produce: compression failed") | |||
else: | else: | |||
print(" produce: finished.") | print(" produce: finished.") | |||
_self.unset("keep_alive") | _self.unset("keep_alive") | |||
if preserve<1: | if preserve<1: | |||
if os.path.isdir(tmp_path): | if os.path.isdir(tmp_path): | |||
for fil in glob.glob(os.path.join(tmp_path,prefix+"*")): | for fil in glob.glob(os.path.join(tmp_path,prefix+"*")): | |||
skipping to change at line 655 | skipping to change at line 803 | |||
os.rmdir(tmp_path) | os.rmdir(tmp_path) | |||
produce_mode_dict = { | produce_mode_dict = { | |||
'normal' : 0, | 'normal' : 0, | |||
'draw' : 1, | 'draw' : 1, | |||
'ray' : 2, | 'ray' : 2, | |||
} | } | |||
produce_mode_sc = cmd.Shortcut(produce_mode_dict.keys()) | produce_mode_sc = cmd.Shortcut(produce_mode_dict.keys()) | |||
def find_exe(exe): | ||||
'''Return full path to executable or None. | ||||
Excludes C:\Windows\System32\convert.exe | ||||
Tests .exe extension on Unix (e.g. for legacy "mpeg_encode.exe" name). | ||||
''' | ||||
from distutils.spawn import find_executable | ||||
path = os.getenv('PATH', '') | ||||
if exe.startswith('convert') and sys.platform == 'win32': | ||||
# filter out C:\Windows\System32 | ||||
path = os.pathsep.join(p | ||||
for p in path.split(os.pathsep) | ||||
if not r'\windows\system32' in p.lower()) | ||||
e = find_executable(exe, path) | ||||
if not e and sys.platform != 'win32': | ||||
e = find_executable(exe + '.exe', path) | ||||
return e | ||||
def produce(filename, mode='', first=0, last=0, preserve=0, | def produce(filename, mode='', first=0, last=0, preserve=0, | |||
encoder='mpeg_encode', quality=-1, quiet=1, | encoder='', quality=-1, quiet=1, | |||
width=0, height=0, _self=cmd): | width=0, height=0, _self=cmd): | |||
''' | ''' | |||
DESCRIPTION | DESCRIPTION | |||
Export a movie to an MPEG file. | Export a movie to an MPEG file. | |||
Requires FREEMOL. | Requires FREEMOL. | |||
ARGUMENTS | ||||
filename = str: filename of MPEG file to produce | ||||
mode = draw or ray: {default: check "ray_trace_frames" setting} | ||||
first = int: first frame to export {default: 1} | ||||
last = int: last frame to export {default: last frame of movie} | ||||
preserve = 0 or 1: don't delete temporary files {default: 0} | ||||
quality = 0-100: encoding quality {default: 90 (movie_quality setting)} | ||||
''' | ''' | |||
try: | from pymol import CmdException | |||
from freemol import mpeg_encode | ||||
except ImportError: | def has_exe(exe): | |||
print(" Error: This PyMOL build is not set up with FREEMOL (freemol.mpeg | return bool(find_exe(exe)) | |||
_encode import failed)") | ||||
return _self.DEFAULT_ERROR | ||||
prefix = _prefix | prefix = _prefix | |||
if _self.is_string(mode): | if mode == '': | |||
if mode == '': | mode = -1 | |||
if cmd.get_setting_boolean('ray_trace_frames'): | elif _self.is_string(mode): | |||
mode = 'ray' | ||||
elif cmd._pymol.invocation.options.no_gui: | ||||
mode = 'ray' | ||||
else: | ||||
mode = 'draw' | ||||
mode = produce_mode_sc.auto_err(mode,"mode") | mode = produce_mode_sc.auto_err(mode,"mode") | |||
mode = produce_mode_dict[mode] | mode = produce_mode_dict[mode] | |||
else: | else: | |||
mode = int(mode) | mode = int(mode) | |||
first = int(first) | first = int(first) | |||
last = int(last) | last = int(last) | |||
quiet = int(quiet) | quiet = int(quiet) | |||
preserve = int(preserve) | preserve = int(preserve) | |||
quality = int(quality) | quality = int(quality) | |||
if quality<0: | if quality<0: | |||
quality = _self.get_setting_int('movie_quality') | quality = _self.get_setting_int('movie_quality') | |||
if quality>100: | if quality>100: | |||
quality = 100 | quality = 100 | |||
ok = 1 | ok = 1 | |||
splitext = os.path.splitext(filename) | splitext = os.path.splitext(filename) | |||
tmp_path = splitext[0]+".tmp" | tmp_path = splitext[0]+".tmp" | |||
if splitext[1]=='': | if splitext[1]=='': | |||
splitext=(splitext[0],'.mpg') | splitext=(splitext[0],'.mpg') | |||
filename = splitext[0]+splitext[1] | filename = splitext[0]+splitext[1] | |||
width, height = int(width), int(height) | ||||
img_ext = '.png' | ||||
# guess encoder | ||||
if not encoder: | ||||
if splitext[1] in ('.mpeg', '.mpg'): | ||||
encoder = 'mpeg_encode' | ||||
elif has_exe('ffmpeg'): | ||||
encoder = 'ffmpeg' | ||||
elif has_exe('convert'): | ||||
encoder = 'convert' | ||||
else: | ||||
raise CmdException('neither "ffmpeg" nor "convert" available for ' | ||||
'video encoding') | ||||
print('using encoder "%s"' % encoder) | ||||
# check encoder | ||||
if encoder == 'mpeg_encode': | ||||
img_ext = '.ppm' | ||||
try: | ||||
from freemol import mpeg_encode | ||||
except ImportError: | ||||
print(" Error: This PyMOL build is not set up with FREEMOL (freemol. | ||||
mpeg_encode import failed)") | ||||
return _self.DEFAULT_ERROR | ||||
elif encoder not in ('ffmpeg', 'convert'): | ||||
raise CmdException('unknown encoder "%s"' % encoder) | ||||
elif not has_exe(encoder): | ||||
raise CmdException('encoder "%s" not available' % (encoder)) | ||||
if img_ext == '.png': | ||||
_self.set('opaque_background', quiet=quiet) | ||||
# MP4 needs dimensions divisible by 2 | ||||
if splitext[1] in ('.mp4', '.mov'): | ||||
if width < 1 or height < 1: | ||||
w, h = _self.get_viewport() | ||||
if width > 0: | ||||
height = width * h / w | ||||
elif height > 0: | ||||
width = height * w / h | ||||
else: | ||||
width, height = w, h | ||||
if width % 2: | ||||
width -= 1 | ||||
if height % 2: | ||||
height -= 1 | ||||
# clean up old files if necessary | ||||
if os.path.exists(filename): | if os.path.exists(filename): | |||
os.unlink(filename) | os.unlink(filename) | |||
if not os.path.exists(tmp_path): | if not os.path.exists(tmp_path): | |||
os.mkdir(tmp_path) | os.mkdir(tmp_path) | |||
elif preserve==0: | elif preserve==0: | |||
# get rid of existing frames (if they exist) | # get rid of existing frames (if they exist) | |||
for fil in glob.glob(os.path.join(tmp_path,prefix+"*")): | for fil in glob.glob(os.path.join(tmp_path,prefix+"*")): | |||
os.unlink(fil) | os.unlink(fil) | |||
if preserve<0: | if preserve<0: | |||
preserve = 0 | preserve = 0 | |||
if os.path.isdir(tmp_path): | if os.path.isdir(tmp_path): | |||
if first <= 0: | if first <= 0: | |||
first = 1 | first = 1 | |||
if last <= 0: | if last <= 0: | |||
last = _self.count_frames() | last = _self.count_frames() | |||
if last <= 1: | if last <= 1: | |||
last = 1 | last = 1 | |||
_self.set("keep_alive") | _self.set("keep_alive") | |||
_self.mpng(os.path.join(tmp_path,prefix+".ppm"),first,last, | _self.mpng(os.path.join(tmp_path,prefix + img_ext),first,last, | |||
preserve,mode=mode,modal=1,quiet=quiet, | preserve,mode=mode,modal=-1,quiet=quiet, | |||
width=width, height=height) | width=width, height=height) | |||
# this may run asynchronously | # this may run asynchronously | |||
else: | else: | |||
ok = 0 | ok = 0 | |||
if ok: | if ok: | |||
t = threading.Thread(target=_encode, | args = ((filename, first, last, preserve, | |||
args=(filename,mode,first,last,preserve, | encoder,tmp_path,prefix,img_ext, | |||
encoder,tmp_path,prefix,quality,quiet,_self)) | quality,quiet,_self)) | |||
t.setDaemon(1) | if _self.get_modal_draw(): | |||
t.start() | t = threading.Thread(target=_encode, args=args) | |||
t.setDaemon(1) | ||||
t.start() | ||||
else: | ||||
_encode(*args) | ||||
if ok: | if ok: | |||
return _self.DEFAULT_SUCCESS | return _self.DEFAULT_SUCCESS | |||
else: | else: | |||
return _self.DEFAULT_ERROR | return _self.DEFAULT_ERROR | |||
End of changes. 27 change blocks. | ||||
31 lines changed or deleted | 262 lines changed or added |