From 1b014b7cd3e896b7f52dd471c5a981245c0b82a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Bozs=C3=B3?= Date: Wed, 10 Jul 2019 06:04:41 +0200 Subject: [PATCH] Rework of gnuplot --- gnuplot/base.py | 487 +++++++++++++++++++++++++++---------------- gnuplot/config.py | 501 --------------------------------------------- gnuplot/private.py | 119 +---------- 3 files changed, 317 insertions(+), 790 deletions(-) delete mode 100644 gnuplot/config.py diff --git a/gnuplot/base.py b/gnuplot/base.py index 4bc6933..0c717c1 100644 --- a/gnuplot/base.py +++ b/gnuplot/base.py @@ -23,94 +23,110 @@ from tempfile import mkstemp from os import fdopen from sys import stderr, platform +from atexit import register +import subprocess as sub -import gnuplot.private as gp - +from .private import color_palettes __all__ = ( "arrow", "call", "colorbar", "debug", "histo", "label", "labels", - "line", "linedef", "margins", "multiplot", "nicer", "obj", - "output", "palette", "plot", "plot_data", "plot_file", "plot_grid", - "ranges", "refresh", "remove_temps", "replot", "reset", "save", + "line", "linedef", "margins", "multiplot", "obj", + "output", "palette", "plot", "data", "file", "grid", + "ranges", "refresh", "replot", "reset", "save", "set", "silent", "splot", "style", "term", "title", "unset_multi", "xlabel", "xrange", "xtics", "ylabel", "yrange", "ytics", - "zlabel", "zrange", "ztics", "colors" + "zlabel", "zrange", "ztics", "colors", "dollar", + "zero", "one", "two", "three", "x", "y", "Symbol" ) -_session = gp.Session() +config = { + "persist": False, + "debug": False, + "silent": False, + "exe": "gnuplot", + "size": (800, 600), + "startup": + """ + set style line 11 lc rgb 'black' lt 1 + set border 3 back ls 11 + set tics nomirror + set style line 12 lc rgb 'black' lt 0 lw 1 + set grid back ls 12 + """ +} -def call(*args): - for arg in args: - _session(arg) +process, multi, debug, silent, plot_items = \ +None, None, config["debug"], config["silent"], [] -def refresh(plot_cmd): - plot_objects = _session.plot_items - - plot_cmds = ", ".join(plot.command for plot in plot_objects) +def startup(): + global process - if _session.debug: - stderr.write("gnuplot> {} {}\n".format(plot_cmd, plot_cmds)) - - _session.write(plot_cmd + " " + plot_cmds + "\n") + if config["persist"]: + cmd = [config["exe"], "--persist"] + else: + cmd = [config["exe"]] + + process = sub.Popen(cmd, stderr=sub.STDOUT, stdin=sub.PIPE) + + +startup() + +def close(): + global process + if multi is not None: + call("unset multiplot") - data = tuple(plot.data for plot in plot_objects - if plot.data is not None) + if process is not None: + process.stdin.close() + retcode = process.wait() + process = None + + +register(close) - if data: - _session.write("\ne\n".join(data) + "\ne\n") - if _session.debug: - stderr.write("\ne\n".join(data) + "\ne\n") +write = process.stdin.write +flush = process.stdin.flush + + +def call(*commands): + global debug - _session.flush() - _session.plot_items = [] + for command in commands: + if debug: + stderr.write("gnuplot> %s\n" % command) + + write(bytes(b"%s\n" % bytes(command, encoding='utf8'))) + flush() +call(config["startup"]) -def _convert_data(data, grid=False, **kwargs): - binary = bool(kwargs.get("binary", True)) - inline = bool(kwargs.get("inline", False)) - if inline and binary: - raise OptionError("Inline binary format is not supported!") +def refresh(plot_cmd): + global plot_items + plot_cmds = ", ".join(plot.command for plot in plot_items) - if binary: - content = data.tobytes() - mode = "wb" - - if grid: - add = " binary matrix" - else: - add = arr_bin(data) - else: - content = np2str(data) - mode = "w" - - if grid: - add = " matrix" - else: - add = "" + if debug: + stderr.write("gnuplot> %s %s\n" % (plot_cmd, plot_cmds)) - if inline: - text = "'-'" - array = content - else: - fd, filename, = mkstemp(text=True) - - f = fdopen(fd, mode) - f.write(content) - f.close() - - _session.temps.append(filename) + call(plot_cmd + " " + plot_cmds + "\n") + + data = tuple(plot.data for plot in plot_items + if plot.data is not None) + + if data: + write("\ne\n".join(data) + "\ne\n") - text = "'{}'".format(filename) - array = None + if debug: + stderr.write("\ne\n".join(data) + "\ne\n") - return array, text + add + flush() + plot_items = [] - -def plot_data(*arrays, ltype="points", **kwargs): + +def data(*arrays, ltype="points", **kwargs): try: data = np.vstack(arrays).T except TypeError: @@ -122,10 +138,10 @@ def plot_data(*arrays, ltype="points", **kwargs): array, text = _convert_data(data, **kwargs) - _session.plot_items.append(PlotDescription(array, text + gp.parse_plot_arguments(**kwargs))) + plot_items.append(PlotDescription(array, text, **kwargs)) -def plot_grid(data, x=None, y=None, **kwargs): +def grid(data, x=None, y=None, **kwargs): data = np.asarray(data, np.float32) @@ -160,35 +176,34 @@ def plot_grid(data, x=None, y=None, **kwargs): array, text = _convert_data(grid, grid=True, **kwargs) - _session.plot_items.append(PlotDescription(array, text + gp.parse_plot_arguments(**kwargs))) + plot_items.append(PlotDescription(array, text, **kwargs)) def line(pos, mode, start=0.0, stop=1.0, ref="graph", **kwargs): add = gp.parse_kwargs(**kwargs) if mode == "h" or mode == "horizontal": - _session("set arrow from {ref} {}, {p} to {ref} {}, {p} nohead {}" - .format(start, stop, " ".join(add), p=pos, ref=ref)) + call("set arrow from {ref} {}, {p} to {ref} {}, {p} nohead {}" + .format(start, stop, " ".join(add), p=pos, ref=ref)) elif mode == "v" or mode == "vertical": - _session("set arrow from {p}, {ref} {} to {p}, {ref} {} nohead {}" - .format(start, stop, " ".join(add), p=pos, ref=ref)) + call("set arrow from {p}, {ref} {} to {p}, {ref} {} nohead {}" + .format(start, stop, " ".join(add), p=pos, ref=ref)) else: raise ValueError('"mode" should be either "h", "horizontal", "v" ' 'or "vertical"') def histo(edges, hist, **kwargs): - title = kwargs.pop("title", None) - edges = edges[:-1] + (edges[1] - edges[0]) / 2.0 vith = "boxes fill solid {}".format(" ".join(gp.parse_kwargs(**kwargs))) - plot_data(edges, hist, title=title, vith=vith) + data(edges, hist, vith=vith, **kwargs) # TODO: rewrite docs -def plot_file(data, matrix=None, binary=None, array=None, endian="default", **kwargs): +def file(data, matrix=None, binary=None, array=None, endian="default", + **kwargs): """ DOCUMENTATION NEEDS TO BE REWORKED! Sets the text to be used for the 'plot' command of Gnuplot for @@ -237,7 +252,7 @@ def plot_file(data, matrix=None, binary=None, array=None, endian="default", **kw """ - if not (isinstance(data, str) or pth.isfile(data)): + if not pth.isfile(data): raise ValueError("data should be a string path to a data file!") text = "'{}'".format(data) @@ -252,43 +267,62 @@ def plot_file(data, matrix=None, binary=None, array=None, endian="default", **kw elif binary: text += " binary" - return PlotDescription(None, text + gp.parse_plot_arguments(**kwargs)) + plot_items.append(PlotDescription(None, text, **kwargs)) def plot(): - if not _session.silent: + if not silent: refresh("plot") def splot(): - if not _session.silent: + if not silent: refresh("splot") -# *********** -# * SETTERS * -# *********** - - -def debug(): - _session.debug = True - -def silent(): - _session.silent = True - +def _convert_data(data, grid=False, **kwargs): + binary = bool(kwargs.get("binary", True)) + inline = bool(kwargs.get("inline", False)) + if inline and binary: + raise OptionError("Inline binary format is not supported!") - -def parse_set(name, value): - if value is True: - return "set {}".format(name) - elif value is False: - return "unset {}".format(name) + if binary: + content = data.tobytes() + mode = "wb" + + if grid: + add = " binary matrix" + else: + add = arr_bin(data) else: - return "set {} {}".format(name, value) + content = np2str(data) + mode = "w" + + if grid: + add = " matrix" + else: + add = "" + + if inline: + text = "'-'" + array = content + else: + fd, filename, = mkstemp(text=True) + + f = fdopen(fd, mode) + f.write(content) + f.close() + + _session.temps.append(filename) + + text = "'{}'".format(filename) + array = None + + return array, text + add def set(**kwargs): - _session(";".join(parse_set(key, value) for key, value in kwargs.items())) + call(";".join(parse_set(key, value) for key, value in kwargs.items())) def size(scale, square=False, ratio=None): @@ -302,11 +336,11 @@ def size(scale, square=False, ratio=None): if ratio is not None: Cmd += " ratio {}".format(ratio) - _session("{} {},{}".format(Cmd, scale[0], scale[1])) + call("{} {},{}".format(Cmd, scale[0], scale[1])) def palette(pal): - _session(gp.color_palettes[pal]) + call(color_palettes[pal]) def margins(screen=False, **kwargs): @@ -318,54 +352,44 @@ def margins(screen=False, **kwargs): for key, value in kwargs.items(): if key in ("lmargin", "rmargin", "tmargin", "bmargin"): - _session(fmt.format(key, value)) + call(fmt.format(key, value)) def multiplot(nplot, **kwargs): - return gp.MultiPlot(nplot, _session, **kwargs) + raise NotImplemented("Multiplot functionality still in the works.") +# return gp.MultiPlot(nplot, _session, **kwargs) def colorbar(cbrange=None, cbtics=None, cbformat=None): if cbrange is not None: - _session("set cbrange [{}:{}]" .format(cbrange[0], cbrange[1])) + call("set cbrange [%f:%s]" % (cbrange[0], cbrange[1])) if cbtics is not None: - _session("set cbtics {}".format(cbtics)) + call("set cbtics %s" % (cbtics)) if cbformat is not None: - _session("set format cb '{}'".format(cbformat)) + call("set format cb '%s'" % (cbformat)) -def unset_multi(self): - _session("unset multiplot") - _session.multi = None - - -def nicer(): - _session( - """ - set style line 11 lc rgb 'black' lt 1 - set border 3 back ls 11 - set tics nomirror - set style line 12 lc rgb 'black' lt 0 lw 1 - set grid back ls 12 - """) +def unset_multi(): + call("unset multiplot") + multi = None def reset(): - _session("reset") + call("reset") def xtics(*args): - _session("set xtics {}".format(parse_range(args))) + call("set xtics %s" % (parse_range(args))) def ytics(*args): - _session("set ytics {}".format(parse_range(args))) + call("set ytics %s" % (parse_range(args))) def ztics(*args): - _session("set ztics {}".format(parse_range(args))) + call("set ztics %s" % (parse_range(args))) def style(stylenum, ltype, **kwargs): @@ -373,19 +397,17 @@ def style(stylenum, ltype, **kwargs): Parameters ---------- """ - _session("set style line {} {}".format(stylenum, linedef(ltype, **kwargs))) + call("set style line %d %s" % (stylenum, linedef(ltype, **kwargs))) def output(outfile, **kwargs): term(**kwargs) - _session("set output '{}'".format(outfile)) + call("set output '%s'" % (outfile)) def title(title): - _session('set title "{}"'.format(title)) - + call('set title "%s"' % (title)) -#def key() def term(term, **kwargs): size = kwargs.get("size") @@ -393,82 +415,82 @@ def term(term, **kwargs): fontsize = float(kwargs.get("fontsize", 12)) enhanced = bool(kwargs.get("enhanced", False)) - txt = "set terminal {}".format(term) + txt = "set terminal %s" % (term) if enhanced: txt += " enhanced" if size is not None: - _session.size = size - txt += " size {},{}".format(size[0], size[1]) + txt += " size %d,%d" % (size[0], size[1]) - _session("{} font '{},{}'".format(txt, font, fontsize)) + call("%s font '%s,%f'" % (txt, font, fontsize)) def arrow(From, to, style=None, tag=""): - temp = "set arrow {} from {},{} to {},{}"\ + temp = "set arrow %s from %f,%f to %f,%f"\ .format(tag, From[0], From[1], to[0], to[1]) if style is not None: - temp += " as {}".format(style) + temp += " as %s".format(style) - _session(temp) + call(temp) def obj(kind): pass -# LABELS - def label(definition, position, **kwargs): font = str(kwargs.get("font", "Verdena")) fontsize = int(kwargs.get("fontsize", 8)) - _session("set label '{}' at {},{} font '{}, {}'" - .format(definition, position[0], position[1], font, fontsize)) + call("set label '%s' at %f,%f font '%s, %d'" + % (definition, position[0], position[1], font, fontsize)) def labels(x="x", y="y", z=None): - _session("set xlabel '{}'".format(x)) - _session("set ylabel '{}'".format(y)) + call("set xlabel '%s'" % (x)) + call("set ylabel '%s'" % (y)) + if z is not None: - _session("set zlabel '{}'".format(z)) + call("set zlabel '%s'" % (z)) + -def xlabel(xlabel="x"): - _session("set xlabel '{}'".format(xlabel)) +def xlabel(label="x"): + call("set xlabel '%s'" % (label)) -def ylabel(ylabel="y"): - _session("set ylabel '{}'".format(ylabel)) +def ylabel(label="y"): + call("set ylabel '%s'" % (label)) -def zlabel(zlabel="z"): - _session("set zlabel '{}'".format(zlabel)) +def zlabel(label="z"): + call("set zlabel '%s'" % (label)) -# RANGES def ranges(x=None, y=None, z=None): if x is not None and len(x) == 2: - _session("set xrange [{}:{}]".format(x[0], x[1])) + call("set xrange [%f:%f]" % (x[0], x[1])) if y is not None and len(y) == 2: - _session("set yrange [{}:{}]".format(y[0], y[1])) + call("set yrange [%f:%f]" % (y[0], y[1])) if z is not None and len(z) == 2: - _session("set zrange [{}:{}]".format(z[0], z[1])) + call("set zrange [%f:%f]" % (z[0], z[1])) + +def xrange(min, max): + call("set xrange [%f:%f]" % (min, max)) -def xrange(xmin, xmax): - _session("set xrange [{}:{}]".format(xmin, xmax)) -def yrange(ymin, ymax): - _session("set yrange [{}:{}]".format(ymin, ymax)) +def yrange(min, max): + call("set yrange [%f:%f]" % (min, max)) + +def zrange(min, max): + call("set zrange [%f:%f]" % (min, max)) -def zrange(zmin, zmax): - _session("set zrange [{}:{}]".format(zmin, zmax)) def replot(): - _session("replot") + call("replot") def save(outfile, **kwargs): @@ -476,24 +498,54 @@ def save(outfile, **kwargs): # terminal and output cannot be changed in multiplotting # if you want to save a multiplot use the output function # before defining multiplot - if _session.multi: - unset_multi() + unset_multi() output(outfile, **kwargs) - _session("replot") + call("replot") -def remove_temps(): - for temp in _session.temps: - remove(temp) +def make_operator(symbol): + tpl = "(%s {} %s)".format(operators[symbol]) - _session.temps = [] + if symbol.startswith("r"): + def f(self, other): + return Symbol(tpl % (other, self)) + else: + def f(self, other): + return Symbol(tpl % (self, other)) + + return f + + +operators = { + "add": "+", "radd": "+", + "sub": "-", "rsub": "-", + "mul": "*", "rmul": "*", + "div": "/", "rdiv": "/", + "truediv": "/", "rtruediv": "/", + "pow": "^", "rpow": "^", + "lt" : "<", "le" : "<=", + "gt" : ">", "ge" : ">=" +} + + + +Symbol = type("Symbol", (str,), {"__%s__" % op: make_operator(op) + for op in operators.keys()}) + + +def dollar(num): + return Symbol("$%d" % num) + + +zero, one, two, three, x, y = \ +dollar(0), dollar(1), dollar(2), dollar(3), Symbol("x"), Symbol("y") class PlotDescription(object): - def __init__(self, data, command): + def __init__(self, data, command, **kwargs): self.data = data - self.command = command + self.command = command + parse_plot_arguments(**kwargs) def __str__(self): return "\nData:\n{}\nCommand: {}\n".format(self.data, self.command) @@ -510,10 +562,10 @@ def __repr__(self): def arr_bin(array, image=False): if array.ndim == 1: - return " binary format='{}'".format(len(array) * gp.fmt_dict[array.dtype]) + return " binary format='%s'" % (len(array) * gp.fmt_dict[array.dtype]) elif array.ndim == 2: fmt = array.shape[1] * gp.fmt_dict[array.dtype] - return " binary record={} format='{}'".format(array.shape[0], fmt) + return " binary record=%d format='%s'" % (array.shape[0], fmt) def np2str(array): @@ -523,14 +575,107 @@ def np2str(array): def linedef(ltype, **kwargs): - parsed_kwargs = (gp.parse_linedef(key, value) + parsed_kwargs = (parse_linedef(key, value) for key, value in kwargs.items()) if ltype is None: return " ".join(parsed_kwargs) else: - return "{} {}".format(ltype, " ".join(parsed_kwargs)) + return "%s %s" % (ltype, " ".join(parsed_kwargs)) + + +def parse_linedef(key, value): + + if key == "pt" or key == "pointtype": + return "%s %s" % (key, point_type_dict[value]) + elif key == "lt" or key == "linetype": + return "%s %s" % (key, line_type_dict[value]) + elif key == "rgb": + return "lc %s '%s'".format(key, value) + + if isinstance(value, bool) and value: + return key + elif isinstance(value, str): + return "%s '%s'" % (key, value) + else: + return "%s %s" % (key, value) + + +def parse_plot_arguments(**kwargs): + vith, using, ptype, title = \ + kwargs.pop("vith", None), kwargs.pop("using", None), \ + kwargs.pop("ptype", "points"), kwargs.pop("title", None) + + text = "" + + if using is not None: + if isinstance(using, tuple): + text = "%s using %s:%s" \ + % (text, proc_using(using[0]), proc_using(using[1])) + else: + text = "%s using (%s)" % (text, proc_using(using)) + + + if title is None: + text += " notitle" + else: + text += " title '%s'" % (title) + + if vith is not None: + text = "%s with %s" % (text, vith) + else: + add = (parse_linedef(key, value) for key, value in kwargs.items()) + text = "%s with %s %s" % (text, ptype, " ".join(add)) + + return text + + +def proc_using(txt): + if "$" in txt: + return "(%s)" % txt + else: + return txt + + +def parse_set(name, value): + if value is True: + return "set {}".format(name) + elif value is False: + return "unset {}".format(name) + else: + return "set {} {}".format(name, value) + + +point_type_dict = { + "dot": 0, + "+": 1, + "x": 2, + "+x": 3, + "empty_square": 4, + "filed_square": 5, + "empty_circle": 6, + "o": 6, + "filled_circle": 7, + "empty_up_triangle": 8, + "filled_up_triangle": 9, + "empty_down_triangle": 10, + "filled_down_triangle": 11, + "empty_rombus": 12, + "filled_rombus": 13, +} + + +line_type_dict = { + "black": -1, + "dashed": 0, + "red": 1, + "green": 2, + "blue": 3, + "purple": 4, + "teal": 5, +} + colors = { "red": "#8b1a0e", diff --git a/gnuplot/config.py b/gnuplot/config.py deleted file mode 100644 index 61c27b7..0000000 --- a/gnuplot/config.py +++ /dev/null @@ -1,501 +0,0 @@ -gnuplot_config = { - "persist": False, - "debug": False, - "silent": False, - "exe": "gnuplot", - "size": (800, 600) -} - - -def update(**kwargs): - gnuplot_config.update(kwargs) - - -if 0: - if platform == "mac": - # TODO: from . import gnuplot_Suites - - import Required_Suite - import aetools - - class _GNUPLOT(aetools.TalkTo, - Required_Suite.Required_Suite, - gnuplot_Suites.gnuplot_Suite, - gnuplot_Suites.odds_and_ends, - gnuplot_Suites.Standard_Suite, - gnuplot_Suites.Miscellaneous_Events): - """Start a gnuplot program and emulate a pipe to it.""" - - def __init__(self): - aetools.TalkTo.__init__(self, '{GP}', start=1) - - elif platform == "win32" or platform == "cli": - try: - from sys import hexversion - except ImportError: - hexversion = 0 - - elif platform[:4] == "java": - from java.lang import Runtime - - - def _test_persist_unix_macosx(gnuplot_command, recognizes_persist): - """ - Determine whether gnuplot recognizes the option '-persist'. - If the configuration variable 'recognizes_persist' is set (i.e., - to something other than 'None'), return that value. Otherwise, - try to determine whether the installed version of gnuplot - recognizes the -persist option. (If it doesn't, it should emit an - error message with '-persist' in the first line.) Then set - 'recognizes_persist' accordingly for future reference. - """ - - if recognizes_persist is None: - g = popen("echo | %s -persist 2>&1" % gnuplot_command, "r") - response = g.readlines() - g.close() - recognizes_persist = not (response and "-persist" in response[0]) - return recognizes_persist - - - class GnuplotProcessUnix(object): - """ - Unsophisticated interface to a running gnuplot program. - This represents a running gnuplot program and the means to - communicate with it at a primitive level (i.e., pass it commands - or data). When the object is destroyed, the gnuplot program exits - (unless the 'persist' option was set). The communication is - one-way; gnuplot's text output just goes to stdout with no attempt - to check it for error messages. - Members: - 'gnuplot' -- the pipe to the gnuplot command. - Methods: - '__init__' -- start up the program. - '__call__' -- pass an arbitrary string to the gnuplot program, - followed by a newline. - 'write' -- pass an arbitrary string to the gnuplot program. - 'flush' -- cause pending output to be written immediately. - 'close' -- close the connection to gnuplot. - """ - - gnuplot_command = "gnuplot" - recognizes_persist = None - prefer_persist = 0 - recognizes_binary_splot = 1 - prefer_inline_data = 0 - default_term = "x11" - prefer_enhanced_postscript = 1 - - def __init__(self, persist=None): - """ - Start a gnuplot process. - Create a 'GnuplotProcess' object. This starts a gnuplot - program and prepares to write commands to it. - Keyword arguments: - 'persist=1' -- start gnuplot with the '-persist' option, - (which leaves the plot window on the screen even after - the gnuplot program ends, and creates a new plot window - each time the terminal type is set to 'x11'). This - option is not available on older versions of gnuplot. - """ - - if persist is None: - persist = self.prefer_persist - if persist: - if not _test_persist_unix_macosx(self.gnuplot_command, - self.recognizes_persist): - raise OptionError("-persist does not seem to be supported " - "by your version of gnuplot!") - self.gnuplot = popen("%s -persist".format(self.gnuplot_command), - "w") - else: - self.gnuplot = popen(self.gnuplot_command, "w") - - # forward write and flush methods: - self.write = self.gnuplot.write - self.flush = self.gnuplot.flush - - def close(self): - if self.gnuplot is not None: - self.gnuplot.close() - self.gnuplot = None - - def __del__(self): - self.close() - - def __call__(self, s): - """Send a command string to gnuplot, followed by newline.""" - - self.write(s + "\n") - self.flush() - - class GnuplotProcessWin32(object): - """ - Unsophisticated interface to a running gnuplot program. - See gp_unix.py for usage information. - """ - - gnuplot_command = r"pgnuplot.exe" - recognizes_persist = 0 - recognizes_binary_splot = 1 - prefer_inline_data = 0 - default_term = "windows" - prefer_enhanced_postscript = 1 - - def __init__(self, persist=0): - """ - Start a gnuplot process. - Create a 'GnuplotProcess' object. This starts a gnuplot - program and prepares to write commands to it. - Keyword arguments: - 'persist' -- the '-persist' option is not supported under - Windows so this argument must be zero. - """ - - if persist: - raise OptionError("-persist is not supported under Windows!") - - self.gnuplot = popen(self.gnuplot_command, "w") - - # forward write and flush methods: - self.write = self.gnuplot.write - self.flush = self.gnuplot.flush - - def close(self): - if self.gnuplot is not None: - self.gnuplot.close() - self.gnuplot = None - - def __del__(self): - self.close() - - def __call__(self, s): - """Send a command string to gnuplot, followed by newline.""" - - self.write(s + "\n") - self.flush() - - def test_persist(self): - return 0 - - class GnuplotProcessMacOSX(object): - """ - Unsophisticated interface to a running gnuplot program. - This represents a running gnuplot program and the means to - communicate with it at a primitive level (i.e., pass it commands - or data). When the object is destroyed, the gnuplot program exits - (unless the 'persist' option was set). The communication is - one-way; gnuplot's text output just goes to stdout with no attempt - to check it for error messages. - Members: - 'gnuplot' -- the pipe to the gnuplot command. - Methods: - '__init__' -- start up the program. - '__call__' -- pass an arbitrary string to the gnuplot program, - followed by a newline. - 'write' -- pass an arbitrary string to the gnuplot program. - 'flush' -- cause pending output to be written immediately. - 'close' -- close the connection to gnuplot. - """ - - gnuplot_command = "gnuplot" - recognizes_persist = None - prefer_persist = 0 - recognizes_binary_splot = 1 - prefer_inline_data = 0 - default_term = "aqua" - prefer_enhanced_postscript = 1 - - def __init__(self, persist=None): - """ - Start a gnuplot process. - Create a 'GnuplotProcess' object. This starts a gnuplot - program and prepares to write commands to it. - Keyword arguments: - 'persist=1' -- start gnuplot with the '-persist' option, - (which leaves the plot window on the screen even after - the gnuplot program ends, and creates a new plot window - each time the terminal type is set to 'x11'). This - option is not available on older versions of gnuplot. - """ - - if persist is None: - persist = self.prefer_persist - if persist: - if not _test_persist_unix_macosx(self.gnuplot_command, - self.prefer_persist): - raise OptionError("-persist does not seem to be supported " - "by your version of gnuplot!") - self.gnuplot = popen("%s -persist" % self.gnuplot_command, - "w") - else: - self.gnuplot = popen(self.gnuplot_command, "w") - - # forward write and flush methods: - self.write = self.gnuplot.write - self.flush = self.gnuplot.flush - - def close(self): - if self.gnuplot is not None: - self.gnuplot.close() - self.gnuplot = None - - def __del__(self): - self.close() - - def __call__(self, s): - """Send a command string to gnuplot, followed by newline.""" - - self.write(s + "\n") - self.flush() - - - class GnuplotProcessMac(object): - """ - Unsophisticated interface to a running gnuplot program. - See gp_unix.GnuplotProcess for usage information. - """ - - recognizes_persist = 0 - recognizes_binary_splot = 1 - prefer_inline_data = 0 - default_term = "pict" - prefer_enhanced_postscript = 1 - - def __init__(self, persist=0): - """ - Start a gnuplot process. - Create a 'GnuplotProcess' object. This starts a gnuplot - program and prepares to write commands to it. - Keyword arguments: - 'persist' -- the '-persist' option is not supported on the - Macintosh so this argument must be zero. - """ - - if persist: - raise OptionError("-persist is not supported on the Macintosh!") - - self.gnuplot = _GNUPLOT() - - def close(self): - if self.gnuplot is not None: - self.gnuplot.quit() - self.gnuplot = None - - def __del__(self): - self.close() - - def write(self, s): - """Mac gnuplot apparently requires '\r' to end statements.""" - - self.gnuplot.gnuexec(s.replace("\n", linesep)) - - def flush(self): - pass - - def __call__(self, s): - """Send a command string to gnuplot, for immediate execution.""" - - # Apple Script doesn't seem to need the trailing '\n'. - self.write(s) - self.flush() - - def test_persist(self): - return 0 - - class GnuplotProcessJava(object): - """ - Unsophisticated interface to a running gnuplot program. - This represents a running gnuplot program and the means to - communicate with it at a primitive level (i.e., pass it commands - or data). When the object is destroyed, the gnuplot program exits - (unless the 'persist' option was set). The communication is - one-way; gnuplot's text output just goes to stdout with no attempt - to check it for error messages. - Members: - Methods: - '__init__' -- start up the program. - '__call__' -- pass an arbitrary string to the gnuplot program, - followed by a newline. - 'write' -- pass an arbitrary string to the gnuplot program. - 'flush' -- cause pending output to be written immediately. - """ - - gnuplot_command = "gnuplot" - recognizes_persist = 1 - prefer_persist = 0 - recognizes_binary_splot = 1 - prefer_inline_data = 0 - default_term = "x11" - prefer_enhanced_postscript = 1 - - def __init__(self, persist=None): - """ - Start a gnuplot process. - Create a 'GnuplotProcess' object. This starts a gnuplot - program and prepares to write commands to it. - Keyword arguments: - 'persist=1' -- start gnuplot with the '-persist' option, - (which leaves the plot window on the screen even after - the gnuplot program ends, and creates a new plot window - each time the terminal type is set to 'x11'). This - option is not available on older versions of gnuplot. - """ - - if persist is None: - persist = self.prefer_persist - - command = [self.gnuplot_command] - - if persist: - if not self.test_persist(): - raise OptionError("-persist does not seem to be supported " - "by your version of gnuplot!") - command.append("-persist") - - # This is a kludge: distutils wants to import everything it - # sees when making a distribution, and if we just call exec() - # normally that causes a SyntaxError in CPython because "exec" - # is a keyword. Therefore, we call the exec() method - # indirectly. - #self.process = Runtime.getRuntime().exec(command) - exec_method = getattr(Runtime.getRuntime(), "exec") - self.process = exec_method(command) - - self.outprocessor = OutputProcessor( - "gnuplot standard output processor", - self.process.getInputStream(), sys.stdout - ) - self.outprocessor.start() - self.errprocessor = OutputProcessor( - "gnuplot standard error processor", - self.process.getErrorStream(), sys.stderr - ) - self.errprocessor.start() - - self.gnuplot = self.process.getOutputStream() - - def close(self): - # ### Does this close the gnuplot process under Java? - if self.gnuplot is not None: - self.gnuplot.close() - self.gnuplot = None - - def __del__(self): - self.close() - - def write(self, s): - self.gnuplot.write(s) - - def flush(self): - self.gnuplot.flush() - - def __call__(self, s): - """Send a command string to gnuplot, followed by newline.""" - - self.write(s + "\n") - self.flush() - - def test_persist(self): - """ - Determine whether gnuplot recognizes the option '-persist'. - """ - - return self.recognizes_persist - - - class GnuplotProcessCygwin(object): - """ - Unsophisticated interface to a running gnuplot program. - See gp_unix.py for usage information. - """ - gnuplot_command = "pgnuplot.exe" - recognizes_persist = 0 - recognizes_binary_splot = 1 - prefer_inline_data = 1 - default_term = "windows" - prefer_enhanced_postscript = 1 - - def __init__(self, persist=0): - """ - Start a gnuplot process. - Create a 'GnuplotProcess' object. This starts a gnuplot - program and prepares to write commands to it. - Keyword arguments: - 'persist' -- the '-persist' option is not supported under - Windows so this argument must be zero. - """ - - if persist: - raise OptionError("-persist is not supported under Windows!") - - self.gnuplot = popen(self.gnuplot_command, 'w') - - # forward write and flush methods: - self.write = self.gnuplot.write - self.flush = self.gnuplot.flush - - def close(self): - if self.gnuplot is not None: - self.gnuplot.close() - self.gnuplot = None - - def __del__(self): - self.close() - - def __call__(self, s): - """Send a command string to gnuplot, followed by newline.""" - - self.write(s + "\n") - self.flush() - - - if platform == "mac": - # TODO: from . import gnuplot_Suites - - import Required_Suite - import aetools - - class _GNUPLOT(aetools.TalkTo, - Required_Suite.Required_Suite, - gnuplot_Suites.gnuplot_Suite, - gnuplot_Suites.odds_and_ends, - gnuplot_Suites.Standard_Suite, - gnuplot_Suites.Miscellaneous_Events): - """Start a gnuplot program and emulate a pipe to it.""" - - def __init__(self): - aetools.TalkTo.__init__(self, '{GP}', start=1) - - gnuplot_proc = GnuplotProcessMac - - elif platform == "win32" or platform == "cli": - try: - from sys import hexversion - except ImportError: - hexversion = 0 - - gnuplot_proc = GnuplotProcessWin32 - - elif platform == "darwin": - - gnuplot_proc = GnuplotProcessMacOSX - - elif platform[:4] == "java": - - from java.lang import Runtime - - gnuplot_proc = GnuplotProcessJava - - elif platform == "cygwin": - - try: - from sys import hexversion - except ImportError: - hexversion = 0 - - - gnuplot_proc = GnuplotProcessCygwin - - else: - gnuplot_proc = GnuplotProcessUnix diff --git a/gnuplot/private.py b/gnuplot/private.py index d510f5c..164b671 100644 --- a/gnuplot/private.py +++ b/gnuplot/private.py @@ -21,54 +21,6 @@ import subprocess as sub -from gnuplot.config import gnuplot_config as gpconf - - -class Session(object): - def __init__(self, persist=False, debug=False, silent=False, **kwargs): - if gpconf["persist"]: - cmd = [gpconf["exe"], "--persist"] - else: - cmd = [gpconf["exe"]] - - self.process = sub.Popen(cmd, stderr=sub.STDOUT, stdin=sub.PIPE) - - self.write = self.process.stdin.write - self.flush = self.process.stdin.flush - - self.persist, self.debug, self.silent = gpconf["persist"], \ - gpconf["debug"], \ - gpconf["silent"] - self.multi, self.closed, self.size = None, False, gpconf["size"] - self.plot_items, self.temps = [], [] - - - def close(self): - if self.process is not None: - self.process.stdin.close() - retcode = self.process.wait() - self.process = None - - - def __del__(self): - if self.multi is not None: - self("unset multiplot") - - self.close() - - for temp in self.temps: - remove(temp) - - - def __call__(self, command): - - if self.debug: - stderr.write("gnuplot> %s\n" % command) - - self.write(bytes(b"%s\n" % bytes(command, encoding='utf8'))) - self.flush() - - class MultiPlot(object): def __init__(self, nplot, session, **kwargs): self.session = ref(session)() @@ -169,55 +121,13 @@ def parse_range(args): return "({})".format(", ".join(str(elem) for elem in args)) -additional_keys = frozenset(("index", "every", "using", "smooth", "axes")) +additional_keys = frozenset({"index", "every", "using", "smooth", "axes"}) def parse_kwargs(**kwargs): return (parse_linedef(key, value) for key, value in kwargs.items()) -def parse_plot_arguments(**kwargs): - _with = kwargs.get("vith") - ptype = kwargs.pop("ptype", "points") - title = kwargs.pop("title", None) - - if _with is not None: - text = " with {}".format(_with) - else: - add = (parse_linedef(key, value) for key, value in kwargs.items()) - text = " with {} {}".format(ptype, " ".join(add)) - - if title is None: - text += " notitle" - else: - text += " title '{}'".format(title) - - return text - - -def parse_linedef(key, value): - - if key == "pt" or key == "pointtype": - return "{} {}".format(key, point_type_dict[value]) - elif key == "lt" or key == "linetype": - return "{} {}".format(key, line_type_dict[value]) - elif key == "rgb": - return "lc {} '{}'".format(key, value) - - - if isinstance(value, bool) and value: - return key - elif isinstance(value, str): - return "{} '{}'".format(key, value) - else: - return "{} {}".format(key, value) - - -# ************************ -# * Private dictionaries * -# ************************ - - fmt_dict = { dtype("float64"): "%float64", dtype("float32"): "%float", @@ -234,33 +144,6 @@ def parse_linedef(key, value): } -point_type_dict = { - "dot": 0, - "+": 1, - "x": 2, - "+x": 3, - "empty_square": 4, - "filed_square": 5, - "empty_circle": 6, - "o": 6, - "filled_circle": 7, - "empty_up_triangle": 8, - "filled_up_triangle": 9, - "empty_down_triangle": 10, - "filled_down_triangle": 11, - "empty_rombus": 12, - "filled_rombus": 13, -} - -line_type_dict = { - "black": -1, - "dashed": 0, - "red": 1, - "green": 2, - "blue": 3, - "purple": 4, - "teal": 5, -} # New matplotlib colormaps by Nathaniel J. Smith, Stefan van der Walt,