#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
This module defines a function "show" that creates a rendering of one
or more parts or partgroups and opens it using the desktop default
application.
"""
import platform
import warnings
import os
import subprocess
from tempfile import NamedTemporaryFile, TemporaryFile
from partitura import save_musicxml
from partitura.io.musescore import render_musescore
__all__ = ["render"]
# def ly_install_msg():
# """Issue a platform specific installation suggestion for lilypond
# """
# if platform.system() == 'Linux':
# s = ('Is lilypond installed? On debian based '
# 'installations you can install it using the '
# 'command "sudo apt install lilypond"')
# elif platform.system() == 'Darwin':
# s = ('Is lilypond installed? Lilypond can be '
# 'installed using brew ( https://brew.sh/ ) '
# 'using the command "brew cask install lilypond"')
# elif platform.system() == 'Windows':
# s = ('Is lilypond installed? It can be downloaded from '
# 'http://lilypond.org/')
# return s
[docs]def render(part, fmt="png", dpi=90, out_fn=None):
"""Create a rendering of one or more parts or partgroups.
The function can save the rendered image to a file (when
`out_fn` is specified), or shown in the default image viewer
application.
Rendering is first attempted through musecore, and if that
fails through lilypond. If that also fails the function returns
without raising an exception.
Parameters
----------
part : :class:`partitura.score.Part` or :class:`partitura.score.PartGroup`
or a list of these
The score content to be displayed
fmt : {'png', 'pdf'}, optional
The image format of the rendered material
out_fn : str or None, optional
The path of the image output file. If None, the rendering will
be displayed in a viewer.
"""
img_fn = render_musescore(part, fmt, out_fn, dpi)
if img_fn is None or not os.path.exists(img_fn):
img_fn = render_lilypond(part, fmt)
if img_fn is None or not os.path.exists(img_fn):
return
if not out_fn:
# NOTE: the temporary image file will not be deleted.
if platform.system() == "Linux":
subprocess.call(["xdg-open", img_fn])
elif platform.system() == "Darwin":
subprocess.call(["open", img_fn])
elif platform.system() == "Windows":
os.startfile(img_fn)
def render_lilypond(part, fmt="png"):
if fmt not in ("png", "pdf"):
print("warning: unsupported output format")
return None
prvw_sfx = ".preview.{}".format(fmt)
with TemporaryFile() as xml_fh, NamedTemporaryFile(
suffix=prvw_sfx, delete=False
) as img_fh:
# save part to musicxml in file handle xml_fh
save_musicxml(part, xml_fh)
# rewind read pointer of file handle before we pass it to musicxml2ly
xml_fh.seek(0)
img_stem = img_fh.name[: -len(prvw_sfx)]
# convert musicxml to lilypond format (use stdout pipe)
cmd1 = ["musicxml2ly", "-o-", "-"]
try:
ps1 = subprocess.run(
cmd1, stdin=xml_fh, stdout=subprocess.PIPE, check=False
)
if ps1.returncode != 0:
warnings.warn(
"Command {} failed with code {}".format(cmd1, ps1.returncode),
stacklevel=2,
)
return None
except FileNotFoundError as f:
warnings.warn(
'Executing "{}" returned {}.'.format(" ".join(cmd1), f),
ImportWarning,
stacklevel=2,
)
return None
# convert lilypond format (read from pipe of ps1) to image, and save to
# temporary filename
cmd2 = [
"lilypond",
"--{}".format(fmt),
"-dno-print-pages",
"-dpreview",
"-o{}".format(img_stem),
"-",
]
try:
ps2 = subprocess.run(cmd2, input=ps1.stdout, check=False)
if ps2.returncode != 0:
warnings.warn(
"Command {} failed with code {}".format(cmd2, ps2.returncode),
stacklevel=2,
)
return None
except FileNotFoundError as f:
warnings.warn(
'Executing "{}" returned {}.'.format(" ".join(cmd2), f),
ImportWarning,
stacklevel=2,
)
return
return img_fh.name