import vcs
import copy
import warnings
import numpy
import os
import time
import _thread
import threading
import glob
from .error import vcsError
def showerror(msg):
raise Exception(msg)
#############################################################################
# #
# Animate wrapper for VCS. #
# #
#############################################################################
[docs]class animate_obj_old(vcs.bestMatch):
"""
Animate the contents of the VCS Canvas. The animation can also be controlled from
the animation GUI. (See VCDAT for more details.)
See the `animation GUI documentation`_
.. _animation GUI documentation: http://www-pcmdi.llnl.gov/software/vcs
"""
##########################################################################
# Initialize the animation flags #
##########################################################################
def __init__(self, vcs_self):
self.vcs_self = vcs_self
self.gui_popup = 0
self.create_flg = 0
self.run_flg = 0
self.continents_value = 0
self.continents_hold_value = 1
# We need to store this because if user close
# anim with preserve_pngs = True
# it still gets deleted at python exit time
self.preserve_pngs = False
##########################################################################
# Create the animation images. If min or max is None, then #
# the animator will find the min and max values from the dataset. #
# If min and max are set to 1e20, then no min and max animation #
# value is used (i.e., each animation frame will have different #
# min and max values. If min and max are set by the user, then #
# these values are used for the animation min and max. #
# If you are running animation from a program, set thread_it to 0. #
# This will cause the Python program to wait for the create function #
# to finish before moving onto the next command line. #
##########################################################################
def create(self, parent=None, min=None, max=None, save_file=None,
thread_it=1, rate=None, bitrate=None, ffmpegoptions=''):
from vcs import minmax
from numpy.ma import maximum, minimum
# Cannot "Run" or "Create" an animation while already creating an
# animation
if self.run_flg == 1:
return
if self.vcs_self.canvas.creating_animation() == 1:
return
if self.vcs_self.animate_info == []:
str = "No data found!"
showerror("Error Message to User", str)
return
# Stop the (thread) execution of the X main loop (if it is running).
self.vcs_self.canvas.stopxmainloop()
# Force VCS to update its orientation, needed when the user changes the
# VCS Canvas size.
self.vcs_self.canvas.updateorientation()
# Make sure the animate information is up-to-date for creating images
if ((self.gui_popup == 1) and (self.create_flg == 0)):
self.update_animate_display_list()
# Save the min and max values for the graphics methods.
# Will need to restore values back when animation is done.
self.save_original_min_max()
# Set up the animation min and max values by changing the graphics method
# Note: cannot set the min and max values if the default graphics
# method is set.
do_min_max = 'yes'
try:
if (parent is not None) and (parent.iso_spacing == 'Log'):
do_min_max = 'no'
except Exception:
pass
# Draw specified continental outlines if needed.
self.continents_hold_value = self.vcs_self.canvas.getcontinentstype()
self.vcs_self.canvas.setcontinentstype(self.continents_value)
if (do_min_max == 'yes'):
minv = []
maxv = []
if (min is None) or (max is None):
for i in range(len(self.vcs_self.animate_info)):
minv.append(1.0e77)
maxv.append(-1.0e77)
for i in range(len(self.vcs_self.animate_info)):
dpy, slab = self.vcs_self.animate_info[i]
mins, maxs = minmax(slab)
minv[i] = float(minimum(float(minv[i]), float(mins)))
maxv[i] = float(maximum(float(maxv[i]), float(maxs)))
if isinstance(min, list) or isinstance(max, list):
for i in range(len(self.vcs_self.animate_info)):
try:
minv.append(min[i])
except Exception:
minv.append(min[-1])
try:
maxv.append(max[i])
except Exception:
maxv.append(max[-1])
else:
for i in range(len(self.vcs_self.animate_info)):
minv.append(min)
maxv.append(max)
# Set the min an max for each plot in the page. If the same graphics method is used
# to display the plots, then the last min and max setting of the
# data set will be used.
for i in range(len(self.vcs_self.animate_info)):
try:
self.set_animation_min_max(minv[i], maxv[i], i)
except Exception:
# if it is default, then you cannot set the min and max, so
# pass.
pass
if save_file is None or save_file.split('.')[-1].lower() == 'ras':
if thread_it:
_thread.start_new_thread(
self.vcs_self.canvas.animate_init, (save_file,))
else:
self.vcs_self.canvas.animate_init(save_file)
else: # ffmpeg stuff
save_info = self.vcs_self.animate_info
animation_info = self.animate_info_from_python()
slabs = []
templates = []
dpys = []
for i in range(len(self.vcs_self.animate_info)):
dpy, slab = self.vcs_self.animate_info[i]
slabs.append(slab)
dpys.append(dpy)
templates.append(dpy.template)
sh = slabs[0].shape
if dpy.g_type in ['boxfill', 'isofill', 'isoline', 'meshfill',
'outfill', 'outline', 'taylordiagram', 'vector', ]:
r = len(sh) - 2
else:
r = len(sh) - 1
# now create the list of all previous indices to plot
indices = []
for i in range(r):
this = list(range(sh[i]))
tmp = []
if indices == []:
for k in this:
indices.append([k, ])
else:
for j in range(len(indices)):
for k in this:
tmp2 = copy.copy(indices[j])
tmp2.append(k)
tmp.append(tmp2)
indices = tmp
count = 1
white_square = self.vcs_self.createfillarea()
white_square.color = 240
white_square.x = [0, 1, 1, 0]
white_square.y = [0, 0, 1, 1]
new_vcs = self.vcs_self
if self.vcs_self.orientation() == 'portrait':
new_vcs.portrait()
# self.vcs_self.close()
for index in indices:
new_vcs.clear()
new_vcs.plot(white_square, bg=1)
for i in range(len(save_info)):
slab = slabs[i]
template = templates[i]
gtype = animation_info["gtype"][i].lower()
gname = animation_info["gname"][i]
gm = None # for flake8 to be happy
loc = locals()
exec("gm = new_vcs.get%s('%s')" % (gtype, gname))
gm = loc["gm"]
for j in index:
slab = slab[j]
new_vcs.plot(slab, gm, new_vcs.gettemplate(template), bg=1)
new_vcs.png("tmp_anim_%i" % count)
count += 1
new_vcs.ffmpeg(
save_file,
"tmp_anim_%d.png",
bitrate=bitrate,
rate=rate,
options=ffmpegoptions)
for i in range(count - 1):
os.remove("tmp_anim_%i.png" % (i + 1))
del(new_vcs)
self.create_flg = 1
def animate_info_from_python(self):
gtype = []
gname = []
tmpl = []
for i in self.vcs_self.animate_info:
d = i[0]
tmpl.append(d.template)
gtype.append(d.g_type)
gname.append(d.g_name)
return {"template": tmpl, "gtype": gtype, "gname": gname}
##########################################################################
# Save original min and max values #
##########################################################################
def save_original_min_max(self):
animation_info = self.animate_info_from_python()
self.save_min = {}
self.save_max = {}
self.save_legend = {}
self.save_levels = {}
self.save_mean_veloc = {}
for i in range(len(self.vcs_self.animate_info)):
gtype = animation_info["gtype"][i].lower()
if gtype == "boxfill":
gm = self.vcs_self.getboxfill(animation_info['gname'][i])
self.save_min[i] = gm.level_1
self.save_max[i] = gm.level_2
# self.save_legend[i] = gm.legend
elif (gtype == "meshfill"):
gm = self.vcs_self.getmeshfill(animation_info['gname'][i])
self.save_levels[i] = gm.levels
elif (gtype == "isofill"):
gm = self.vcs_self.getisofill(animation_info['gname'][i])
self.save_levels[i] = gm.levels
elif (gtype == "isoline"):
gm = self.vcs_self.getisoline(animation_info['gname'][i])
self.save_levels[i] = gm.levels
elif (gtype == "yxvsx"):
gm = self.vcs_self.getyxvsx(animation_info['gname'][i])
self.save_min[i] = gm.datawc_y1
self.save_max[i] = gm.datawc_y2
elif (gtype == "xyvsy"):
gm = self.vcs_self.getxyvsy(animation_info['gname'][i])
self.save_min[i] = gm.datawc_x1
self.save_max[i] = gm.datawc_x2
elif (gtype == "vector"):
gm = self.vcs_self.getvector(animation_info['gname'][i])
self.save_mean_veloc[i] = gm.reference
##########################################################################
# Restore min and max values #
##########################################################################
def restore_min_max(self):
animation_info = self.animate_info_from_python()
try:
for i in range(len(self.vcs_self.animate_info)):
gtype = animation_info["gtype"][i].lower()
if gtype == "boxfill":
gm = self.vcs_self.getboxfill(animation_info['gname'][i])
gm.level_1 = self.save_min[i]
gm.level_2 = self.save_max[i]
# gm.legend = self.save_legend[i]
elif (gtype == "meshfill"):
gm = self.vcs_self.getmeshfill(animation_info['gname'][i])
gm.levels = self.save_levels[i]
elif (gtype == "isofill"):
gm = self.vcs_self.getisofill(animation_info['gname'][i])
gm.levels = self.save_levels[i]
elif (gtype == "isoline"):
gm = self.vcs_self.getisoline(animation_info['gname'][i])
gm.levels = self.save_levels[i]
elif (gtype == "yxvsx"):
gm = self.vcs_self.getyxvsx(animation_info['gname'][i])
gm.datawc_y1 = self.save_min[i]
gm.datawc_y2 = self.save_max[i]
elif (gtype == "xyvsy"):
gm = self.vcs_self.getxyvsy(animation_info['gname'][i])
gm.datawc_x1 = self.save_min[i]
gm.datawc_x2 = self.save_max[i]
elif (gtype == "vector"):
gm = self.vcs_self.getvector(animation_info['gname'][i])
gm.reference = self.save_mean_veloc[i]
except Exception:
pass
##########################################################################
# Set the animation min and max values #
##########################################################################
def set_animation_min_max(self, min, max, i):
from vcs import mkscale, mklabels, getcolors
animation_info = self.animate_info_from_python()
gtype = animation_info["gtype"][i].lower()
levs = mkscale(min, max)
dic = mklabels(levs)
cols = getcolors(levs)
if gtype == "boxfill":
gm = self.vcs_self.getboxfill(animation_info['gname'][i])
if gm.boxfill_type == 'custom':
gm.fillareacolors = cols
gm.levels = levs
else:
gm.level_1 = levs[0]
gm.level_2 = levs[-1]
gm.legend = None
elif (gtype == "meshfill"):
gm = self.vcs_self.getmeshfill(animation_info['gname'][i])
if (min == 1e20) and (max == 1e20):
gm.levels = (1e20, 1e20)
else:
gm.levels = levs
gm.fillareacolors = cols
elif (gtype == "isofill"):
gm = self.vcs_self.getisofill(animation_info['gname'][i])
if (min == 1e20) and (max == 1e20):
gm.levels = (1e20, 1e20)
else:
gm.levels = levs
gm.fillareacolors = cols
elif (gtype == "isoline"):
gm = self.vcs_self.getisoline(animation_info['gname'][i])
if (min == 1e20) and (max == 1e20):
gm.levels = (1e20, 1e20)
else:
gm.levels = levs
gm.fillareacolors = cols
elif (gtype == "yxvsx"):
gm = self.vcs_self.getyxvsx(animation_info['gname'][i])
if (min != 1e20) and (max != 1e20):
gm.yticlabels1 = dic
gm.yticlabels2 = dic
min = levs[0]
max = levs[-1]
gm.datawc_y1 = min
gm.datawc_y2 = max
elif (gtype == "xyvsy"):
gm = self.vcs_self.getxyvsy(animation_info['gname'][i])
if (min != 1e20) and (max != 1e20):
gm.xticlabels1 = dic
gm.xticlabels2 = dic
min = levs[0]
max = levs[-1]
gm.datawc_x1 = min
gm.datawc_x2 = max
elif (gtype == "vector"):
gm = self.vcs_self.getvector(animation_info['gname'][i])
mean_veloc = 1e20
if (min != 1e20) and (max != 1e20):
mean_veloc = float(int(numpy.sqrt((min ** 2) + (max ** 2))))
gm.reference = mean_veloc
animation_info['gname'][i] = gm.name
##########################################################################
# Return the animation min and max values #
##########################################################################
def return_animation_min_max(self):
dpy, slab = self.vcs_self.animate_info[0]
return vcs.minmax(slab)
##########################################################################
# Load animation from a stored Raster file. #
##########################################################################
def load_from_file(self, parent=None, load_file=None, thread_it=1):
if os.access(load_file, os.R_OK) == 0:
showerror(
"Error Message to the User",
"The specfied file does not have read permission or does not exist. "
"Please check the availability of the file.")
return
if thread_it == 1:
_thread.start_new_thread(
self.vcs_self.canvas.animate_load, (load_file,))
else:
self.vcs_self.canvas.animate_init(load_file)
self.create_flg = 1
##########################################################################
# Creating animation flag #
##########################################################################
def creating_animation_flg(self):
return self.vcs_self.canvas.creating_animation()
##########################################################################
# Run animation flag #
##########################################################################
def run_animation_flg(self):
return self.run_flg
##########################################################################
# Run or start the animation #
##########################################################################
def run(self):
# Cannot "Create" an animation while running an animation.
if self.vcs_self.canvas.creating_animation() == 1:
return
if ((self.create_flg == 1) and (self.run_flg == 0)):
self.run_flg = 1
self.vcs_self.canvas.animate_run()
##########################################################################
# Stop the animation creation #
##########################################################################
def stop_create(self):
if (self.create_flg == 1):
self.vcs_self.canvas.animate_stop_create()
##########################################################################
# Stop the animation #
##########################################################################
def stop(self):
if (self.create_flg == 1) and (self.run_flg == 1):
self.run_flg = 0
self.vcs_self.canvas.animate_stop()
elif (self.create_flg == 1):
self.vcs_self.canvas.animate_stop_create()
##########################################################################
# View the specified animation frame #
##########################################################################
def frame(self, value=1):
if (self.create_flg == 1) and (self.run_flg == 0):
self.vcs_self.canvas.animate_frame(value)
##########################################################################
# Return the number of animate frames #
##########################################################################
def number_of_frames(self):
return self._number_of_frames
##########################################################################
# Pause the animation loop #
# Value ranges from 0 to 100 #
##########################################################################
def pause(self, value=1):
if (((not isinstance(value, int))) or (value not in list(range(0, 101)))):
raise vcsError(
"Pause value must be between an integer between 0 and 100.")
if (self.create_flg == 1) and (self.run_flg == 1):
self.vcs_self.canvas.animate_pause(value)
##########################################################################
# Zoom in on the animation #
# Value ranges from 0 to 20 #
##########################################################################
def zoom(self, value=1):
if (((not isinstance(value, int))) or (value not in list(range(1, 21)))):
raise vcsError(
"Zoom value must be between an integer between 1 and 20.")
if self.vcs_self.canvas.creating_animation() == 1:
return
if self.create_flg == 1:
self.vcs_self.canvas.animate_zoom(value)
##########################################################################
# Pan the zoomed animation or frame in the x (or horizontal) direction #
# Value ranges from -100 to 100 #
##########################################################################
def horizontal(self, value=0):
if (((not isinstance(value, int))) or (value not in list(range(-100, 101)))):
raise vcsError(
"Horizontal pan value must be between an integer between -100 and 100.")
if self.vcs_self.canvas.creating_animation() == 1:
return
if self.create_flg == 1:
self.vcs_self.canvas.animate_horizontal(value)
##########################################################################
# Pan the zoomed animation or frame in the y (or vertical) direction #
# Value ranges from -100 to 100 #
##########################################################################
def vertical(self, value=0):
if (((not isinstance(value, int))) or (value not in list(range(-100, 101)))):
raise vcsError(
"Vertical pan value must be between an integer between -100 and 100.")
if self.vcs_self.canvas.creating_animation() == 1:
return
if self.create_flg == 1:
self.vcs_self.canvas.animate_vertical(value)
##########################################################################
# Set the direction of the animation: #
# Value 1 -> forward, 2 -> backward #
##########################################################################
def direction(self, value=1):
if (((not isinstance(value, int))) or (value not in list(range(1, 3)))):
raise vcsError(
"Direction value must be between either 1='forward' or 2='backward'.")
if self.vcs_self.canvas.creating_animation() == 1:
return
if self.create_flg == 1:
self.vcs_self.canvas.animate_direction(value)
##########################################################################
# Mode sets the cycle, forth and back, or animate once #
# Value: 1 -> cycle, 2 -> animate once, and 3 -> forth and back #
##########################################################################
def mode(self, value=1):
if (((not isinstance(value, int))) or (value not in [1, 3])):
raise vcsError("Mode value must be between either 1 or 3.")
if value == 2:
self.run_flg = 0
if self.vcs_self.canvas.creating_animation() == 1:
return
if self.create_flg == 1:
self.vcs_self.canvas.animate_mode(value)
##########################################################################
# Update the animation display list #
##########################################################################
def update_animate_display_list(self):
current_display_list = self.vcs_self.return_display_names()
temp_list = []
for i in range(len(self.vcs_self.animate_info)):
if self.vcs_self.animate_info[i][0].name in current_display_list:
temp_list.append((self.vcs_self.animate_info[i][0],
self.vcs_self.animate_info[i][1]))
self.vcs_self.animate_info = temp_list
##########################################################################
# Close the animate session #
##########################################################################
def close(self, preserve_pngs=False):
if self.create_flg == 1:
self.vcs_self.canvas.animate_close()
self.gui_popup = 0
self.create_flg = 0
self.run_flg = 0
self.vcs_self.canvas.getcontinentstype(self.continents_hold_value)
self.continents_value = 0
self.continents_hold_value = 1
self.vcs_self.animate_info = []
# Now that the animation is completed, restore the graphics methods min
# and max values.
self.restore_min_max()
if hasattr(self, "_unique_prefix"):
png_names = glob.glob(
os.path.join(
os.path.expanduser("~"),
".uvcdat",
self._unique_prefix,
"anim_*.png")
)
if preserve_pngs:
self.preserve_pngs = True
if not self.preserve_pngs:
for f in png_names:
os.remove(f)
if len(png_names) > 0:
os.rmdir(os.path.dirname(png_names[-1]))
else:
return png_names
class RT:
def __init__(self, nextFunc, parent):
self.next = nextFunc
self.running = True
self.parent = parent
def start(self):
self.runnnig = True
while self.running:
next(self)
time.sleep(1. / self.parent.frames_per_second)
def stop(self):
self.running = False
# Adapted from
# http://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread-in-python
[docs]class StoppableThread(threading.Thread):
def __init__(self):
super(StoppableThread, self).__init__()
self._stopper = threading.Event()
self._running = threading.Event()
self._running.set()
def stop(self):
self._stopper.set()
def is_stopped(self):
return self._stopper.isSet()
def pause(self):
self._running.clear()
def resume(self):
self._running.set()
def wait_if_paused(self):
self._running.wait()
class AnimationCreateParams(vcs.bestMatch):
def __init__(self, a_min=None, a_max=None, axis=0):
self.a_min = a_min
self.a_max = a_max
self.axis = axis
[docs]class AnimationCreate(StoppableThread):
def __init__(self, controller):
StoppableThread.__init__(self)
self.controller = controller
[docs] def run(self):
self.controller.initialize_create_canvas()
self.controller.set_anim_min_max()
all_args = self.controller.get_all_frame_args()
self.controller.reset_file_paths()
self._really_used = []
self._backend_kargs = []
for i, args in enumerate(all_args):
if self.is_stopped():
break
self.wait_if_paused()
if self._really_used != []:
for j, a in enumerate(args):
args[j] = a[:-3] + self._really_used[j]
kargs = self._backend_kargs
else:
kargs = []
displays = self.controller.render_frame(args, i, kargs)
if self._really_used == []:
for d in displays:
self._really_used.append(
[d._gettemplate(), d._getg_type(), d._getg_name()])
self._backend_kargs.append(d.backend)
# this is how you allow the GUI to process events during
# animation creation
# time.sleep(0.001)
self.controller.restore_min_max()
self.controller.animation_created = True
if self.controller.signals is not None:
self.controller.signals.created.emit()
class AnimationPlaybackParams(vcs.bestMatch):
def __init__(self):
self.zoom_factor = 1.0
self.vertical_factor = 0
self.horizontal_factor = 0
self.frames_per_second = 10.0
self.loop = True
def fps(self, value=None):
"""Animation desired number of frame per seconds (might not be
achievable depending on your system)
"""
if value is not None:
value = max(value, 0.5)
self.frames_per_second = value
return self
return self.frames_per_second
def zoom(self, value):
"""Zoom factor for the animation"""
if value > 0:
self.zoom_factor = value
else:
raise Exception("Zoom must be greater than 0")
def horizontal(self, value):
""" Pan the window horizontaly (when zoomed). 100% means move so you can see the furthest right part of the picture"
"""
if value > 100.:
raise Exception("Horizontal Factor cannot be greater than 100%")
if value < -100.:
raise Exception("Horizontal Factor cannot be less than 100%")
self.horizontal_factor = value
def vertical(self, value):
""" Pan the window verticaly (when zoomed). 100% means move so you can see the top part of the picture"
"""
if value > 100.:
raise Exception("Vertical Factor cannot be greater than 100%")
if value < -100.:
raise Exception("Vertical Factor cannot be less than 100%")
self.vertical_factor = value
[docs]class AnimationPlayback(StoppableThread):
def __init__(self, controller):
StoppableThread.__init__(self)
self.controller = controller
[docs] def run(self):
self.controller.frame_num = 0
if self.controller.signals is not None:
self.controller.signals.stopped.emit(False)
self.controller.playback_running = True
while not self.is_stopped():
self.wait_if_paused()
# draw frame
# print "DRAWING FRAME:", self.controller.frame_num,
# self.controller.animation_files[self.frame_num]
self.controller.draw_frame()
self.controller.frame_num += 1
if self.controller.frame_num >= self.controller.number_of_frames():
if self.controller.playback_params.loop:
self.controller.frame_num = 0
else:
break
time.sleep(1. / self.controller.playback_params.frames_per_second)
self.controller.playback_running = False
if self.controller.signals is not None:
self.controller.signals.stopped.emit(True)
[docs]class AnimationController(animate_obj_old):
def __init__(self, vcs_self):
animate_obj_old.__init__(self, vcs_self)
self.create_thread = None
self.create_canvas = None
self.playback_thread = None
self.animation_created = False
self.playback_running = False
self.animation_files = []
self.animation_seed = None
self.frame_num = 0
self.create_params = AnimationCreateParams()
self.playback_params = AnimationPlaybackParams()
# GUI will set these if available
self.signals = None
self.AnimationCreate = AnimationCreate
self.AnimationPlayback = AnimationPlayback
self._number_of_frames = None
def close(self, preserve_pngs=False):
if self.create_canvas:
self.create_canvas.close()
self.create_canvas = None
self.animate_info = []
return super(AnimationController, self).close(preserve_pngs)
def set_signals(self, signals):
self.signals = signals
def created(self):
return self.animation_created
def create(self, thread_it=True, min=None, max=None):
self.generate_number_of_frames()
if thread_it == 0:
thread_it = False
if self.create_thread is None or not self.create_thread.is_alive():
self.canvas_info = self.vcs_self.canvasinfo()
self.animate_info = self.vcs_self.animate_info
if min is not None or max is not None:
raise RuntimeError(
"Animation min/max autoset has been deprecated, use graphic method to set them")
self.create_params.a_min = None
self.create_params.a_max = None
self.create_thread = self.AnimationCreate(self)
self.create_thread.start()
if not thread_it:
self.create_thread.join()
def create_stop(self):
self.create_thread.stop()
def create_pause(self):
self.create_thread.pause()
def create_resume(self):
self.create_thread.resume()
def is_playing(self):
return self.playback_running
def playback(self):
if (self.created() and
(self.playback_thread is None or not self.playback_thread.is_alive())):
self.playback_thread = self.AnimationPlayback(self)
self.playback_thread.start()
# alias run to playback for command-line compatibility
run = playback
def playback_stop(self):
if self.is_playing():
self.playback_thread.stop()
def stop(self):
if self.is_playing():
self.playback_thread.stop()
self.playback_thread.join()
self.playback_thread = None
else:
self.create_thread.stop()
self.create_thread.join()
def playback_pause(self):
if self.is_playing():
self.playback_thread.pause()
def playback_resume(self):
if self.playback_thread is not None:
self.playback_thread.resume()
def pause(self, value=1):
self.playback_pause()
time.sleep(value)
self.playback_resume()
def number_of_frames(self):
return self._number_of_frames
def initialize_create_canvas(self):
# create a new canvas for each frame
self.create_canvas = vcs.init()
self.create_canvas.setcolormap(self.vcs_self.getcolormapname())
dims = self.canvas_info
if dims['height'] < 500:
factor = 2
else:
factor = 1
self.create_canvas.setbgoutputdimensions(width=dims['width'] * factor,
height=dims[
'height'] * factor,
units='pixel')
def set_anim_min_max(self):
# Save the min and max values for the graphics methods.
# Will need to restore values back when animation is done.
self.save_original_min_max()
minv = []
maxv = []
if (self.create_params.a_min is None or
self.create_params.a_max is None):
for i in range(len(self.animate_info)):
minv.append(1.0e77)
maxv.append(-1.0e77)
for i in range(len(self.animate_info)):
dpy, slab = self.animate_info[i]
mins, maxs = vcs.minmax(slab)
minv[i] = float(numpy.minimum(float(minv[i]), float(mins)))
maxv[i] = float(numpy.maximum(float(maxv[i]), float(maxs)))
elif (isinstance(self.create_params.a_min, list) or
isinstance(self.create_params.a_max, list)):
for i in range(len(self.animate_info)):
try:
minv.append(self.create_params.a_min[i])
except Exception:
minv.append(self.create_params.a_min[-1])
try:
maxv.append(self.create_params.a_max[i])
except Exception:
maxv.append(self.create_params.a_max[-1])
else:
for i in range(len(self.animate_info)):
minv.append(self.create_params.a_min)
maxv.append(self.create_params.a_max)
# Set the min an max for each plot in the page. If the same graphics method is used
# to display the plots, then the last min and max setting of the
# data set will be used.
for i in range(len(self.animate_info)):
try:
self.set_animation_min_max(minv[i], maxv[i], i)
except Exception:
# if it is default, then you cannot set the min and max, so
# pass.
pass
def generate_number_of_frames(self):
if self._number_of_frames is not None:
return self._number_of_frames
NFrames = 1.e8
for info in self.vcs_self.animate_info:
disp, slabs = info
if slabs[0] is None:
# Nothing to do
continue
if disp.g_type == "meshfill":
try:
g = slabs[0].getGrid()
NXY = len(g.shape)
except Exception:
# No grid so slab1 rnk will tell us
NXY = slabs[1].ndim - 2 # lat/lon/vertices
elif disp.g_type in ["G1D"]:
NXY = 1
else:
NXY = 2
# Now we can do the math and figure how many frames
NXtraDims = slabs[0].ndim - NXY
n = 1
for a in slabs[0].getAxisList()[:NXtraDims]:
n *= len(a)
# We truncate to mininum number of common frames
NFrames = min(n, NFrames)
self._number_of_frames = NFrames
self._number_of_dims_used_for_plot = NXY
return self._number_of_frames
def get_all_frame_args(self):
alen = None
truncated = False
vcs_ai = list(self.animate_info)
for I in vcs_ai:
if alen is None:
alen = I[1][0].shape[self.create_params.axis]
else:
len_tmp = I[1][0].shape[self.create_params.axis]
if len_tmp != alen:
alen = numpy.minimum(alen, len_tmp)
truncated = True
if truncated:
warnings.warn("Because of inconsistent shapes over axis: %i, the "
"animation length will be truncated to: %i\n" %
(self.create_params.axis, alen))
all_args = []
for i in range(alen):
# y.clear()
frameArgs = []
for I in vcs_ai:
d = I[0]
kw = {}
n = len(I[1][0].shape)
for j, id in enumerate(I[1][0].getAxisIds()):
if j != self.create_params.axis and j < n - 2:
kw[id] = slice(0, 1)
elif j == self.create_params.axis:
kw[id] = slice(i, i + 1)
else:
break
args = [I[1][0](**kw), ]
if I[1][1] is not None:
kw = {}
n = len(I[1][1].shape)
for j, id in enumerate(I[1][1].getAxisIds()):
if j != self.create_params.axis and j < n - 2:
kw[id] = slice(0, 1)
elif j == self.create_params.axis:
kw[id] = slice(i, i + 1)
else:
break
args.append(I[1][1](**kw))
args += [d.template, d.g_type, d.g_name]
frameArgs.append(args)
all_args.append(frameArgs)
return all_args
def reset_file_paths(self):
if self.animation_seed is not None:
if self.animation_files != []:
for fnm in self.animation_files:
os.remove(fnm)
self.animation_files = []
self.animation_seed = None
def render_frame(self, frame_args, frame_num, frame_kargs=[]):
if self.animation_seed is None:
self.animation_seed = numpy.random.randint(10000000000)
fn = os.path.join(os.path.expanduser("~"), ".uvcdat",
"__uvcdat_%i_%i.png" % (self.animation_seed, frame_num))
self.animation_files.append(fn)
# BB: this clearing and replotting somehow fixes vcs internal state
# and prevents segfaults when running multiple animations
# self.vcs_self.replot()
self.create_canvas.clear()
displays = []
for iarg, args in enumerate(frame_args):
if len(frame_kargs) > iarg:
kargs = frame_kargs[iarg]
else:
kargs = {}
displays.append(self.create_canvas.plot(*args, bg=1, **kargs))
self.create_canvas.png(fn, draw_white_background=1)
return displays
def draw_frame(self, frame_num=None):
if frame_num is not None:
self.frame_num = frame_num
self.vcs_self.backend.clear()
self.vcs_self.put_png_on_canvas(
self.animation_files[self.frame_num],
self.playback_params.zoom_factor,
self.playback_params.vertical_factor,
self.playback_params.horizontal_factor)
if self.signals is not None:
self.signals.drawn.emit(self.frame_num)
[docs] def save(self, movie, bitrate=1024, rate=None, options=None):
"""Save animation to a file"""
if self.created():
while len(self.animation_files) != self.number_of_frames():
self.draw_frame()
self.frame_num = (self.frame_num + 1) % self.number_of_frames()
if rate is None:
rate = self.playback_params.fps()
files = os.path.join(
os.path.dirname(
self.animation_files[0]),
"anim_%d.png")
self.vcs_self.ffmpeg(movie, files, bitrate, rate, options)
self.animation_files = []
[docs] def fps(self, value=None):
"""Animation desired number of frame per seconds (might not be
achievable depending on your system)
"""
return self.playback_params.fps(value)
[docs] def zoom(self, value):
"""Zoom factor for the animation"""
self.playback_params.zoom(value)
[docs] def horizontal(self, value):
""" Pan the window horizontaly (when zoomed). 100% means move so you can see the furthest right part of the picture"
"""
self.playback_params.horizontal(value)
[docs] def vertical(self, value):
""" Pan the window verticaly (when zoomed). 100% means move so you can see the top part of the picture"
"""
self.playback_params.vertical(value)