Source code for metawards.analysis._animate_plots


from typing import List as _List

__all__ = ["animate_plots", "import_animate_modules"]


def import_font_modules():
    """Imports matplotlib.font_manager"""
    try:
        from matplotlib import font_manager
        return font_manager
    except ImportError as e:
        print("Unable to import the matplotlib.font_manager module...")
        print(f"Error is {e}")
        return None


[docs]def import_animate_modules(): """Import the python modules needed to animate plots. This imports Python Pillow Returns ------- (Image, ImageDraw) The PIL.Image and PIL.ImageDraw modules """ try: from PIL import Image, ImageDraw, ImageFont except ImportError: print("Could not import Python Pillow to create animated plot.") print("Please install using 'pip install Pillow>=6.2.1',") print("or by running 'metawards-install --optional'") return (Image, ImageDraw, ImageFont)
[docs]def animate_plots(plots: _List[str], output: str, delay: int = 500, ordering: str = "fingerprint", verbose=False): """Animate the plots contained in the filenames 'plots', writing the output to 'output'. Creates an animated gif of the plots and writes them to the file 'output' Parameters ---------- plots: List[str] The list of files containing graphs to plot output: str The name of the file to write the animated gif to delay: int The delay in milliseconds between frames. Default is 500 milliseconds (2 frames per second) which is reasonable for animated graphs ordering: str The ordering to use for the frames. This can be 'fingerprint', 'filename' or 'custom' verbose: bool Whether to print out information to the screen during processing """ if ordering == "fingerprint": from .._variableset import VariableSet if verbose: print("Arranging into fingerprint order...") fingerprints = {} filenames = [] legends = {} for plot in plots: try: (values, repeat_idx) = VariableSet.extract_values(plot) except Exception as e: raise ValueError( f"Could not extract a fingerprint from '{plot}'. " f"Error was {e.__class__} {e}") if len(values) == 0 and repeat_idx is None: # no fingerprint - write these first in filename order filenames.append(plot) legends[plot] = None continue if repeat_idx: legends[plot] = f"{values} x {repeat_idx}" values.append(repeat_idx) else: legends[plot] = str(values) values.append(0) # now create a sortable string from these values key = " ".join([("%.5f" % x) for x in values]) fingerprints[key] = plot keys = list(fingerprints.keys()) keys.sort() plots = [] for key in keys: plots.append(fingerprints[key]) filenames.sort() plots = plots + filenames elif ordering == "filename": if verbose: print("Arranging into filename order...") plots.sort() legends = {} for plot in plots: legends[plot] = plot elif ordering == "custom": if verbose: print("Using the user-supplied order...") legends = {} for plot in plots: legends[plot] = plot else: print(f"Could not recognise ordering scheme {ordering}") raise ValueError(f"Unrecognised ordering scheme {ordering}") if output is None: import os common = os.path.commonprefix(plots) if not (common.endswith("_")): common += "_" output = f"{common}animate.gif" if verbose: print(f"Animating the below frame in this order, with a delay " f"between frames of {delay} ms.") for i, plot in enumerate(plots): print(f"{i+1}: {plot}") print(f"Output will be written to {output}") # open all of the images images = [] (Image, ImageDraw, ImageFont) = import_animate_modules() for plot in plots: image = Image.open(plot) if legends[plot]: try: font_manager = import_font_modules() if font_manager: if verbose: print(f"writing {legends[plot]}") fontfile = font_manager.findfont("Arial") font = ImageFont.truetype(fontfile, size=28) draw = ImageDraw.Draw(image) draw.text((0, 0), legends[plot], (0, 0, 0), font=font) except Exception: # couldn't tag it - don't break the animation pass images.append(image) images[0].save(output, save_all=True, append_images=images[1:], optimize=False, duration=delay, loop=0) # now try to optimize the gif try: from pygifsicle import optimize have_optimize = True except Exception: have_optimize = False if have_optimize: if verbose: print("Optimizing the resulting gif...") try: optimize(output) except Exception: if verbose: print("Optimisation failed - using unoptimized gif") return output