Adding EPS and PNG formats, and changing the UI to allow all formats to be output simultaneously.

Sun, 27 Feb 2011 15:48:06 +0100

author
Andy Buckley <andy@insectnation.org>
date
Sun, 27 Feb 2011 15:48:06 +0100
changeset 131
12a1338fa626
parent 130
bac6642d4d30
child 132
73d0449105a5

Adding EPS and PNG formats, and changing the UI to allow all formats to be output simultaneously.

ChangeLog file | annotate | diff | comparison | revisions
pyslha.py file | annotate | diff | comparison | revisions
slhaplot file | annotate | diff | comparison | revisions
     1.1 --- a/ChangeLog	Sun Feb 27 15:44:13 2011 +0100
     1.2 +++ b/ChangeLog	Sun Feb 27 15:48:06 2011 +0100
     1.3 @@ -1,5 +1,17 @@
     1.4  2011-02-27  Andy Buckley  <andy@insectnation.org>
     1.5  
     1.6 +	* Version 1.2.0 (major version change due to interface changes and
     1.7 +	major new format functionality)
     1.8 +
     1.9 +	* Change --outfile to --outname since multiple simultaneous
    1.10 +	formats are now supported and a single output name can't get all
    1.11 +	the file extensions correct.
    1.12 +
    1.13 +	* Add EPS and PNG formats, and allow all (apart from TeX fragment)
    1.14 +	formats to be produced with a single command.
    1.15 +
    1.16 +	* Version 1.1.0
    1.17 +
    1.18  	* Improving the newly-fixed HERWIG <-> PDG particle ID translation
    1.19  	with a pair of functions, pdgid2herwigid and herwigid2pdgid, which
    1.20  	are publicly available in the pyslha API.
     2.1 --- a/pyslha.py	Sun Feb 27 15:44:13 2011 +0100
     2.2 +++ b/pyslha.py	Sun Feb 27 15:48:06 2011 +0100
     2.3 @@ -11,9 +11,9 @@
     2.4  The current release supports SLHA version 1. Assistance with supporting version
     2.5  2 will be gladly accepted!
     2.6  
     2.7 -The plotting script provides output in PDF via LaTeX and the TikZ graphics package,
     2.8 -and as LaTeX/TikZ source for direct embedding into documents or user-tweaking of
     2.9 -the generated output.
    2.10 +The plotting script provides output in PDF, EPS and PNG via LaTeX and the TikZ
    2.11 +graphics package, and as LaTeX/TikZ source for direct embedding into documents or
    2.12 +user-tweaking of the generated output.
    2.13  
    2.14  TODOs:
    2.15   * Identify HERWIG decay matrix element to use in ISAWIG
    2.16 @@ -23,7 +23,7 @@
    2.17  """
    2.18  
    2.19  __author__ = "Andy Buckley <andy.buckley@cern.ch"
    2.20 -__version__ = "1.1.0"
    2.21 +__version__ = "1.2.0"
    2.22  
    2.23  
    2.24  def _autotype(var):
     3.1 --- a/slhaplot	Sun Feb 27 15:44:13 2011 +0100
     3.2 +++ b/slhaplot	Sun Feb 27 15:48:06 2011 +0100
     3.3 @@ -8,14 +8,18 @@
     3.4  it will be assumed to be an SLHA file (for which the normal extension is .spc).
     3.5  
     3.6  Output is currently rendered via the LaTeX PGF/TikZ graphics package: this may
     3.7 -be obtained as PDF (by default) or as LaTeX source which can be edited or
     3.8 -compiled into any LaTeX-supported form.
     3.9 +be obtained as PDF (by default), EPS, PNG or as LaTeX source which can be edited or
    3.10 +compiled into any LaTeX-supported form. By default the output file name(s) are
    3.11 +the same as the input names but with the file extension replaced with one appropriate
    3.12 +to the output file format.
    3.13  
    3.14  TODOs:
    3.15 -  * EPS and PNG output
    3.16 +  * Merge labels if shifting fails (cf. "poi" test spectrum file).
    3.17 +  * Avoid overlap/too-tight clustering of y-axis ticks. Looks fine for *most* spectra with 100 GeV intervals.
    3.18    * Allow user to provide a file which defines the particle line x-positions, labels, etc.
    3.19 -  * Allow user control over aspect ratio / geometry
    3.20 -  * Use proper distinction between physical, plot-logical, and plot output coords
    3.21 +  * Allow use of --outname to specify a list of output base names for multiple inputs.
    3.22 +  * Use proper distinction between physical, plot-logical, and plot output coords.
    3.23 +  * Allow user control over aspect ratio / geometry.
    3.24    * Use scaling to allow the y coordinates to be in units of 100 GeV in TikZ output.
    3.25    * Distribute decay arrow start/end positions along mass lines rather than always to/from their centres?
    3.26  """
    3.27 @@ -77,18 +81,25 @@
    3.28  import pyslha
    3.29  import sys, optparse, logging
    3.30  parser = optparse.OptionParser(usage=__doc__, version=pyslha.__version__)
    3.31 -parser.add_option("-o", "--out", metavar="FILE",
    3.32 -                  help="write output to FILE (only allowed if only one input file is specified!)",
    3.33 -                  dest="OUTFILE", default=None)
    3.34 +parser.add_option("-o", "--outname", metavar="NAME",
    3.35 +                  help="write output to NAME.suffix, i.e. the suffix will be automatically "
    3.36 +                  "generated and the argument to this command now just specifies the base "
    3.37 +                  "of the output to which the extension is appended: this allows multiple "
    3.38 +                  "formats to be written simultaneously. If you provide a file extension "
    3.39 +                  "as part of the NAME argument, it will be treated as part of the base "
    3.40 +                  "name and another extension will be automatically added. Note that this "
    3.41 +                  "option can only be used if only processing a single input file, as you "
    3.42 +                  "presumably don't want all the input spectra to overwrite each other!",
    3.43 +                  dest="OUTNAME", default=None)
    3.44  parser.add_option("-f", "--format", metavar="FORMAT",
    3.45 -                  choices=["tikz", "tikzfrag", "tikzpdf"],
    3.46 -                  help="format in which to write output. 'tikz' produces LaTeX source using the "
    3.47 -                  "TikZ graphics package to render the plot, 'tikzfrag' produces the same but "
    3.48 +                  help="format in which to write output. 'tex' produces LaTeX source using the "
    3.49 +                  "TikZ graphics package to render the plot, 'texfrag' produces the same but "
    3.50                    "with the LaTeX preamble and document lines commented out to make it directly "
    3.51 -                  "includeable as a code fragment in LaTeX document source, and 'tikzpdf' produces "
    3.52 -                  "a PDF file created by running pdflatex and pdfcrop on the 'tikz' output"
    3.53 -                  "(default: %default)",
    3.54 -                  dest="FORMAT", default="tikzpdf")
    3.55 +                  "includeable as a code fragment in LaTeX document source, and 'pdf' produces "
    3.56 +                  "a PDF file created by running pdflatex and pdfcrop on the 'tex' output. You "
    3.57 +                  "may also combine multiple formats just by listing them all in the format "
    3.58 +                  "string, e.g. 'png,pdf,tex' (default: %default)",
    3.59 +                  dest="FORMAT", default="pdf")
    3.60  parser.add_option("--preamble", metavar="FILE",
    3.61                    help="specify a file to be inserted into LaTeX output as a special preamble",
    3.62                    dest="PREAMBLE", default=None)
    3.63 @@ -118,19 +129,35 @@
    3.64  parser.add_option_group(verbgroup)
    3.65  
    3.66  
    3.67 -## Run parser and sort out a few resulting details
    3.68 +## Run parser and configure the logging level
    3.69  opts, args = parser.parse_args()
    3.70 -#
    3.71 +logging.basicConfig(level=opts.LOGLEVEL, format="%(message)s")
    3.72 +
    3.73 +## Create some boolean flags from the chosen particle label clash-avoidance scheme
    3.74  opts.PARTICLES_LABELS_SHOW = (opts.PARTICLES_LABELS != "none")
    3.75  opts.PARTICLES_LABELS_MERGE = (opts.PARTICLES_LABELS == "merge")
    3.76  opts.PARTICLES_LABELS_SHIFT = (opts.PARTICLES_LABELS == "shift")
    3.77 -#
    3.78 +
    3.79 +## Parsing the branching ratio string
    3.80  if opts.DECAYS_MINBR.endswith("%"):
    3.81      opts.DECAYS_MINBR = float(opts.DECAYS_MINBR[:-1]) / 100.0
    3.82  else:
    3.83      opts.DECAYS_MINBR = float(opts.DECAYS_MINBR)
    3.84 -#
    3.85 -logging.basicConfig(level=opts.LOGLEVEL, format="%(message)s")
    3.86 +
    3.87 +## Output format wrangling
    3.88 +if "tikz" not in opts.FORMAT:
    3.89 +    opts.FORMAT = "tikz" + opts.FORMAT
    3.90 +if opts.FORMAT == "tikz":
    3.91 +    opts.FORMAT = "tikztex"
    3.92 +elif opts.FORMAT == "tikzfrag":
    3.93 +    opts.FORMAT = "tikztexfrag"
    3.94 +if "frag" in opts.FORMAT and any(f in opts.FORMAT for f in ["pdf", "eps", "png"]):
    3.95 +    logging.error("Oops! You can't currently use LaTeX fragment output together with graphics "
    3.96 +                  "formats, since the graphics can't be built from the incomplete LaTeX "
    3.97 +                  "file. We'll fix this, but for now you will have to run slhaplot twice: "
    3.98 +                  "once for the LaTeX fragment, and another time for the graphical output "
    3.99 +                  "formats. Exiting...")
   3.100 +    sys.exit(1)
   3.101  
   3.102  
   3.103  ## Check non-optional arguments
   3.104 @@ -138,21 +165,16 @@
   3.105  if len(INFILES) == 0:
   3.106      parser.print_help()
   3.107      sys.exit(1)
   3.108 -if len(INFILES) > 1 and opts.OUTFILE is not None:
   3.109 -    logging.error("Multiple input files specified with --outfile... not a good plan! Exiting for your own good...")
   3.110 +if len(INFILES) > 1 and opts.OUTNAME is not None:
   3.111 +    logging.error("Multiple input files specified with --outname... not a good plan! Exiting for your own good...")
   3.112      sys.exit(1)
   3.113  
   3.114  
   3.115  ## Test for external packages
   3.116  import subprocess
   3.117 -if opts.FORMAT == "tikzpdf":
   3.118 -    ## Test for pdflatex
   3.119 -    p = subprocess.Popen(["which", "pdflatex"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
   3.120 -    rtn = p.wait()
   3.121 -    if rtn != 0:
   3.122 -        logging.error("pdflatex could not be found: tikzpdf format mode cannot work")
   3.123 -        sys.exit(3)
   3.124 -    ## Test for tikz package
   3.125 +
   3.126 +## Test for tikz package if rendering the LaTeX source
   3.127 +if not any(f in opts.FORMAT for f in ["pdf", "eps", "png"]):
   3.128      p = subprocess.Popen(["which", "kpsewhich"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
   3.129      rtn = p.wait()
   3.130      if rtn != 0:
   3.131 @@ -161,32 +183,65 @@
   3.132          p = subprocess.Popen(["kpsewhich", "tikz.sty"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
   3.133          rtn = p.wait()
   3.134          if rtn != 0:
   3.135 -            logging.error("tikz.sty could not be found: tikzpdf format mode cannot work")
   3.136 +            logging.error("LaTeX tikz.sty could not be found: graphical format modes cannot work")
   3.137              sys.exit(3)
   3.138  
   3.139 +## Test for pdflatex if we need to make a PDF
   3.140 +if "pdf" in opts.FORMAT or "png" in opts.FORMAT:
   3.141 +    p = subprocess.Popen(["which", "pdflatex"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
   3.142 +    rtn = p.wait()
   3.143 +    if rtn != 0:
   3.144 +        logging.error("pdflatex could not be found: PDF format mode (and dependent ones like PNG) cannot work")
   3.145 +        sys.exit(3)
   3.146 +
   3.147 +    ## Test for convert if we need to make a bitmap format
   3.148 +    if "png" in opts.FORMAT:
   3.149 +        p = subprocess.Popen(["which", "convert"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
   3.150 +        rtn = p.wait()
   3.151 +        if rtn != 0:
   3.152 +            logging.error("convert could not be found: PNG format mode cannot work")
   3.153 +            sys.exit(3)
   3.154 +
   3.155 +## Test for latex, dvips and ps2eps if making an EPS
   3.156 +if "eps" in opts.FORMAT:
   3.157 +    p = subprocess.Popen(["which", "latex"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
   3.158 +    rtn = p.wait()
   3.159 +    if rtn != 0:
   3.160 +        logging.error("latex could not be found: EPS format mode cannot work")
   3.161 +        sys.exit(3)
   3.162 +    p = subprocess.Popen(["which", "dvips"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
   3.163 +    rtn = p.wait()
   3.164 +    if rtn != 0:
   3.165 +        logging.error("dvips could not be found: EPS format mode cannot work")
   3.166 +        sys.exit(3)
   3.167 +    p = subprocess.Popen(["which", "ps2eps"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
   3.168 +    rtn = p.wait()
   3.169 +    if rtn != 0:
   3.170 +        logging.error("ps2eps could not be found: EPS format mode cannot work")
   3.171 +        sys.exit(3)
   3.172 +
   3.173  
   3.174  ## Loop over input spectrum files
   3.175  for infile in INFILES:
   3.176  
   3.177      ## Choose output file
   3.178 -    outfile = opts.OUTFILE
   3.179 -    if outfile is None:
   3.180 +    outname = opts.OUTNAME
   3.181 +    if outname is None:
   3.182          import os
   3.183          o = os.path.basename(infile)
   3.184          if o == "-":
   3.185              o = "out"
   3.186 -        if "." in o:
   3.187 +        elif "." in o:
   3.188              o = o[:o.rindex(".")]
   3.189 -        ## Add format-specific suffix
   3.190 -        suffix = ".tex"
   3.191 -        if "pdf" in opts.FORMAT:
   3.192 -            suffix = ".pdf"
   3.193 -        # TODO: add EPS and PNG support
   3.194 -        outfile = o + suffix
   3.195 -
   3.196 +        outname = o
   3.197  
   3.198      ## Info for the user
   3.199 -    logging.info("Plotting %s -> %s" % (infile, outfile))
   3.200 +    # TODO: Print all output filenames explicitly?
   3.201 +    extlist = [f for f in ["tex", "pdf", "eps", "png"] if f in opts.FORMAT]
   3.202 +    extstr = ",".join(extlist)
   3.203 +    if len(extlist) > 1:
   3.204 +        extstr = "{" + extstr + "}"
   3.205 +    logging.info("Plotting %s -> %s.%s" % (infile, outname, extstr))
   3.206  
   3.207  
   3.208      ## Read spectrum file
   3.209 @@ -376,7 +431,7 @@
   3.210  
   3.211          ## Comment out the preamble etc. if only the TikZ fragment is wanted
   3.212          c = ""
   3.213 -        if opts.FORMAT == "tikzfrag":
   3.214 +        if opts.FORMAT == "tikztexfrag":
   3.215              c = "%"
   3.216  
   3.217          ## Write LaTeX header
   3.218 @@ -473,7 +528,6 @@
   3.219                          (scalethickness(dd.br), scalecolor(dd.br) or dd.color,
   3.220                           scalex(dd.xyfrom[0]), yscale*dd.xyfrom[1], scalex(dd.xyto[0]), yscale*dd.xyto[1])
   3.221  
   3.222 -
   3.223          ## Draw mass lines
   3.224          if PDETAILS:
   3.225              out += "\n  %% Particle lines\n"
   3.226 @@ -499,25 +553,70 @@
   3.227  
   3.228  
   3.229          ## Write output
   3.230 -        if opts.FORMAT != "tikzpdf":
   3.231 -            writeout(out, outfile)
   3.232 -        else:
   3.233 -            ## Run LaTeX and pdfcrop
   3.234 +        if "tex" in opts.FORMAT:
   3.235 +            writeout(out, outname+".tex")
   3.236 +
   3.237 +        if any(f in opts.FORMAT for f in ["pdf", "eps", "png"]):
   3.238 +            ## Run LaTeX
   3.239              import tempfile, shutil, subprocess
   3.240              tmpdir = tempfile.mkdtemp()
   3.241              writeout(out, os.path.join(tmpdir, "mytmp.tex"))
   3.242 -            ok = True
   3.243 -            try:
   3.244 -                p = subprocess.Popen(["pdflatex", "\scrollmode\input", "mytmp.tex"],
   3.245 -                                     stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=tmpdir)
   3.246 -                p.wait()
   3.247 -                shutil.copyfile(os.path.join(tmpdir, "mytmp.pdf"), outfile)
   3.248 -            except Exception, e:
   3.249 -                logging.error("pdflatex could not be run: tikzpdf format mode cannot work")
   3.250 -                ok = False
   3.251 +
   3.252 +            def mktmpprocess(cmdlist):
   3.253 +                "Convenience method to reduce repeated subprocess.Popen call noise in what follows."
   3.254 +                return subprocess.Popen(cmdlist, cwd=tmpdir,
   3.255 +                                        stdout=subprocess.PIPE, stderr=subprocess.PIPE)
   3.256 +
   3.257 +            ## Processing via PDF
   3.258 +            if any(f in opts.FORMAT for f in ["pdf", "png"]):
   3.259 +                try:
   3.260 +                    p = mktmpprocess(["pdflatex", r"\scrollmode\input", "mytmp.tex"])
   3.261 +                    p.wait()
   3.262 +                    if "pdf" in opts.FORMAT:
   3.263 +                        shutil.copyfile(os.path.join(tmpdir, "mytmp.pdf"), outname+".pdf")
   3.264 +                except Exception, e:
   3.265 +                    logging.error("pdflatex could not be run: PDF and/or PNG format mode cannot work")
   3.266 +                    shutil.rmtree(tmpdir)
   3.267 +                    sys.exit(3)
   3.268 +                    # TODO: Can we use a finally block to make sure that the tmpdir is cleared up, despite the 'sys.exit's?
   3.269 +
   3.270 +                ## Turning the PDF into a PNG if required
   3.271 +                if "png" in opts.FORMAT:
   3.272 +                    try:
   3.273 +                        p = mktmpprocess(["convert", "-density", "150", "-flatten", "mytmp.pdf", "mytmp.png"])
   3.274 +                        p.wait()
   3.275 +                        shutil.copyfile(os.path.join(tmpdir, "mytmp.png"), outname+".png")
   3.276 +                    except Exception, e:
   3.277 +                        logging.error("convert could not be run: PNG format mode cannot work")
   3.278 +                        shutil.rmtree(tmpdir)
   3.279 +                        sys.exit(3)
   3.280 +
   3.281 +            ## Making a PS or EPS
   3.282 +            if any(f in opts.FORMAT for f in ["eps"]):
   3.283 +                try:
   3.284 +                    p = mktmpprocess(["latex", r"\scrollmode\input", "mytmp.tex"])
   3.285 +                    p.wait()
   3.286 +                except Exception, e:
   3.287 +                    logging.error("latex could not be run: EPS format mode cannot work")
   3.288 +                    shutil.rmtree(tmpdir)
   3.289 +                    sys.exit(3)
   3.290 +                try:
   3.291 +                    p = mktmpprocess(["dvips", "mytmp.dvi", "-o", "mytmp.ps"])
   3.292 +                    p.wait()
   3.293 +                except Exception, e:
   3.294 +                    logging.error("dvips could not be run: EPS format mode cannot work")
   3.295 +                    shutil.rmtree(tmpdir)
   3.296 +                    sys.exit(3)
   3.297 +                try:
   3.298 +                    p = mktmpprocess(["ps2eps", "mytmp.ps"])
   3.299 +                    p.wait()
   3.300 +                    shutil.copyfile(os.path.join(tmpdir, "mytmp.eps"), outname+".eps")
   3.301 +                except Exception, e:
   3.302 +                    logging.error("ps2eps could not be run: EPS format mode cannot work")
   3.303 +                    shutil.rmtree(tmpdir)
   3.304 +                    sys.exit(3)
   3.305 +
   3.306              shutil.rmtree(tmpdir)
   3.307 -            if not ok:
   3.308 -                sys.exit(3)
   3.309  
   3.310  
   3.311      ## UNRECOGNISED FORMAT!

mercurial