1
2
3
4
5
6
7
8 """_Gnuplot.py -- An object that represents a running gnuplot process.
9
10 This file implements the Gnuplot plotter object, which is an abstract
11 interface to a running gnuplot process.
12
13 """
14
15 import sys, string, types
16
17 from . import gp, PlotItems, termdefs, Errors
18
19 from PyFoam.ThirdParty.six import string_types
20 from PyFoam.ThirdParty.six.moves import input as rinput
21
23 """A file to which gnuplot commands can be written.
24
25 Sometimes it is convenient to write gnuplot commands to a command
26 file for later evaluation. In that case, one of these objects is
27 used as a mock gnuplot process. Note that temporary files may be
28 deleted before you have time to execute the file!
29
30 Members:
31
32 'gnuplot' -- the file object gathering the commands.
33
34 Methods:
35
36 '__init__' -- open the file.
37
38 '__call__' -- write a gnuplot command to the file, followed by a
39 newline.
40
41 'write' -- write an arbitrary string to the file.
42
43 'flush' -- cause pending output to be written immediately.
44
45 """
46
48 """Open the file for writing."""
49
50 self.gnuplot = open(filename, 'w')
51
52 self.write = self.gnuplot.write
53 self.flush = self.gnuplot.flush
54
56 """Write a command string to the file, followed by newline."""
57
58 self.write(s + '\n')
59 self.flush()
60
61
63 """Interface to a gnuplot program.
64
65 A Gnuplot represents a higher-level interface to a gnuplot
66 program. It can plot 'PlotItem's, which represent each thing to
67 be plotted on the current graph. It keeps a reference to each of
68 the 'PlotItems' used in the current plot, so that they (and their
69 associated temporary files) are not deleted prematurely.
70
71 Members:
72
73 'itemlist' -- a list of the PlotItems that are associated with
74 the current plot. These are deleted whenever a new plot
75 command is issued via the 'plot' method.
76
77 'plotcmd' -- 'plot' or 'splot', depending on what was the last
78 plot command.
79
80 Methods:
81
82 '__init__' -- if a filename argument is specified, the
83 commands will be written to that file instead of being
84 piped to gnuplot.
85
86 'plot' -- clear the old plot and old 'PlotItems', then plot
87 the arguments in a fresh plot command. Arguments can be:
88 a 'PlotItem', which is plotted along with its internal
89 options; a string, which is plotted as a 'Func'; or
90 anything else, which is plotted as a 'Data'.
91
92 'splot' -- like 'plot', except for 3-d plots.
93
94 'hardcopy' -- replot the plot to a postscript file (if
95 filename argument is specified) or pipe it to the printer
96 as postscript othewise. If the option 'color' is set to
97 true, then output color postscript.
98
99 'replot' -- replot the old items, adding any arguments as
100 additional items as in the plot method.
101
102 'refresh' -- issue (or reissue) the plot command using the
103 current 'PlotItems'.
104
105 '__call__' -- pass an arbitrary string to the gnuplot process,
106 followed by a newline.
107
108 'xlabel', 'ylabel', 'zlabel', 'title' -- set corresponding plot
109 attribute.
110
111 'interact' -- read lines from stdin and send them, one by one,
112 to the gnuplot interpreter. Basically you can type
113 commands directly to the gnuplot command processor.
114
115 'load' -- load a file (using the gnuplot 'load' command).
116
117 'save' -- save gnuplot commands to a file (using gnuplot
118 'save' command) If any of the 'PlotItem's is a temporary
119 file, it will be deleted at the usual time and the save
120 file will be pretty useless :-).
121
122 'clear' -- clear the plot window (but not the itemlist).
123
124 'reset' -- reset all gnuplot settings to their defaults and
125 clear the current itemlist.
126
127 'set_string' -- set or unset a gnuplot option whose value is a
128 string.
129
130 '_clear_queue' -- clear the current 'PlotItem' list.
131
132 '_add_to_queue' -- add the specified items to the current
133 'PlotItem' list.
134
135 """
136
137
138
139
140 optiontypes = {
141 'title' : 'string',
142 'xlabel' : 'string',
143 'ylabel' : 'string',
144 'zlabel' : 'string',
145 'xrange' : 'range',
146 'yrange' : 'range',
147 'zrange' : 'range',
148 'trange' : 'range',
149 'urange' : 'range',
150 'vrange' : 'range',
151 'parametric' : 'boolean',
152 'polar' : 'boolean',
153 'output' : 'string',
154 }
155
156 - def __init__(self, filename=None, persist=None, debug=0):
157 """Create a Gnuplot object.
158
159 Create a 'Gnuplot' object. By default, this starts a gnuplot
160 process and prepares to write commands to it.
161
162 Keyword arguments:
163
164 'filename=<string>' -- if a filename is specified, the
165 commands are instead written to that file (e.g., for
166 later use using 'load').
167
168 'persist=1' -- start gnuplot with the '-persist' option
169 (which creates a new plot window for each plot command).
170 (This option is not available on older versions of
171 gnuplot.)
172
173 'debug=1' -- echo the gnuplot commands to stderr as well as
174 sending them to gnuplot.
175
176 """
177
178 if filename is None:
179 self.gnuplot = gp.GnuplotProcess(persist=persist)
180 else:
181 if persist is not None:
182 raise Errors.OptionError(
183 'Gnuplot with output to file does not allow '
184 'persist option.')
185 self.gnuplot = _GnuplotFile(filename)
186 self._clear_queue()
187 self.debug = debug
188 self.plotcmd = 'plot'
189 self('set terminal %s' % (gp.GnuplotOpts.default_term,))
190
192
193
194
195 if self.gnuplot is not None:
196 self.gnuplot.close()
197 self.gnuplot = None
198
202
204 """Send a command string to gnuplot.
205
206 Send the string s as a command to gnuplot, followed by a
207 newline. All communication with the gnuplot process (except
208 for inline data) is through this method.
209
210 """
211
212 self.gnuplot(s)
213 if self.debug:
214
215 sys.stderr.write('gnuplot> %s\n' % (s,))
216
218 """Refresh the plot, using the current 'PlotItem's.
219
220 Refresh the current plot by reissuing the gnuplot plot command
221 corresponding to the current itemlist.
222
223 """
224
225 plotcmds = []
226 for item in self.itemlist:
227 plotcmds.append(item.command())
228 self(self.plotcmd + ' ' + ", ".join(plotcmds))
229 for item in self.itemlist:
230
231 item.pipein(self.gnuplot)
232 self.gnuplot.flush()
233
235 """Clear the 'PlotItems' from the queue."""
236
237 self.itemlist = []
238
240 """Add a list of items to the itemlist (but don't plot them).
241
242 'items' is a sequence of items, each of which should be a
243 'PlotItem' of some kind, a string (interpreted as a function
244 string for gnuplot to evaluate), or a numpy array (or
245 something that can be converted to a numpy array).
246
247 """
248
249 for item in items:
250 if isinstance(item, PlotItems.PlotItem):
251 self.itemlist.append(item)
252 elif isinstance(item,string_types):
253 self.itemlist.append(PlotItems.Func(item))
254 else:
255
256 self.itemlist.append(PlotItems.Data(item))
257
258 - def plot(self, *items, **keyw):
259 """Draw a new plot.
260
261 Clear the current plot and create a new 2-d plot containing
262 the specified items. Each arguments should be of the
263 following types:
264
265 'PlotItem' (e.g., 'Data', 'File', 'Func') -- This is the most
266 flexible way to call plot because the PlotItems can
267 contain suboptions. Moreover, PlotItems can be saved to
268 variables so that their lifetime is longer than one plot
269 command; thus they can be replotted with minimal overhead.
270
271 'string' (e.g., 'sin(x)') -- The string is interpreted as
272 'Func(string)' (a function that is computed by gnuplot).
273
274 Anything else -- The object, which should be convertible to an
275 array, is passed to the 'Data' constructor, and thus
276 plotted as data. If the conversion fails, an exception is
277 raised.
278
279 """
280
281 if keyw:
282 self.set(**keyw)
283
284 self.plotcmd = 'plot'
285 self._clear_queue()
286 self._add_to_queue(items)
287 self.refresh()
288
289 - def splot(self, *items, **keyw):
290 """Draw a new three-dimensional plot.
291
292 Clear the current plot and create a new 3-d plot containing
293 the specified items. Arguments can be of the following types:
294
295 'PlotItem' (e.g., 'Data', 'File', 'Func', 'GridData' ) -- This
296 is the most flexible way to call plot because the
297 PlotItems can contain suboptions. Moreover, PlotItems can
298 be saved to variables so that their lifetime is longer
299 than one plot command--thus they can be replotted with
300 minimal overhead.
301
302 'string' (e.g., 'sin(x*y)') -- The string is interpreted as a
303 'Func()' (a function that is computed by gnuplot).
304
305 Anything else -- The object is converted to a Data() item, and
306 thus plotted as data. Note that each data point should
307 normally have at least three values associated with it
308 (i.e., x, y, and z). If the conversion fails, an
309 exception is raised.
310
311 """
312
313 if keyw:
314 self.set(**keyw)
315
316 self.plotcmd = 'splot'
317 self._clear_queue()
318 self._add_to_queue(items)
319 self.refresh()
320
321 - def replot(self, *items, **keyw):
322 """Replot the data, possibly adding new 'PlotItem's.
323
324 Replot the existing graph, using the items in the current
325 itemlist. If arguments are specified, they are interpreted as
326 additional items to be plotted alongside the existing items on
327 the same graph. See 'plot' for details.
328
329 """
330
331 if keyw:
332 self.set(**keyw)
333
334 self._add_to_queue(items)
335 self.refresh()
336
338 """Allow user to type arbitrary commands to gnuplot.
339
340 Read stdin, line by line, and send each line as a command to
341 gnuplot. End by typing C-d.
342
343 """
344
345 import time
346 if sys.platform == 'win32':
347 sys.stderr.write('Press Ctrl-z twice to end interactive input\n')
348 else:
349
350 sys.stderr.write('Press C-d to end interactive input\n')
351 while 1:
352 try:
353 line = rinput('gnuplot>>> ')
354 except EOFError:
355 break
356 self(line)
357 time.sleep(0.2)
358 sys.stderr.write('\n')
359
361 """Clear the plot window (without affecting the current itemlist)."""
362
363 self('clear')
364
366 """Reset all gnuplot settings to their defaults and clear itemlist."""
367
368 self('reset')
369 self.itemlist = []
370
371 - def load(self, filename):
372 """Load a file using gnuplot's 'load' command."""
373
374 self("load '%s'" % (filename,))
375
376 - def save(self, filename):
377 """Save the current plot commands using gnuplot's 'save' command."""
378
379 self("save '%s'" % (filename,))
380
382 """Set a string option, or if s is omitted, unset the option."""
383
384 if s is None:
385 self('set %s' % (option,))
386 else:
387 self('set %s "%s"' % (option, s))
388
389 - def set_label(self, option, s=None, offset=None, font=None):
390 """Set or clear a label option, which can include an offset or font.
391
392 If offset is specified, it should be a tuple of two integers
393 or floats.
394
395 If font is specified, it is appended to the command as a
396 string in double quotes. Its interpretation is
397 terminal-dependent; for example, for postscript it might be
398 'Helvetica,14' for 14 point Helvetica.
399
400 """
401
402 cmd = ['set', option]
403 if s is not None:
404 cmd.append('"%s"' % (s,))
405
406 if offset is not None:
407 cmd.append('%s,%s' % offset)
408
409 if font is not None:
410 cmd.append('"%s"' % (font,))
411
412 self(" ".join(cmd))
413
415 """Set an on/off option. It is assumed that the way to turn
416 the option on is to type `set <option>' and to turn it off,
417 `set no<option>'."""
418
419 if value:
420 self('set %s' % option)
421 else:
422 self('set no%s' % option)
423
425 """Set a range option (xrange, yrange, trange, urange, etc.).
426 The value can be a string (which is passed as-is, without
427 quotes) or a tuple (minrange,maxrange) of numbers or string
428 expressions recognized by gnuplot. If either range is None
429 then that range is passed as `*' (which means to
430 autoscale)."""
431
432 if value is None:
433 self('set %s [*:*]' % (option,))
434 elif isinstance(value,string_types):
435 self('set %s %s' % (option, value,))
436 else:
437
438 (minrange,maxrange) = value
439 if minrange is None:
440 minrange = '*'
441 if maxrange is None:
442 maxrange = '*'
443 self('set %s [%s:%s]' % (option, minrange, maxrange,))
444
445 - def set(self, **keyw):
446 """Set one or more settings at once from keyword arguments.
447 The allowed settings and their treatments are determined from
448 the optiontypes mapping."""
449
450 for (k,v) in list(keyw.items()):
451 try:
452 type = self.optiontypes[k]
453 except KeyError:
454 raise 'option %s is not supported' % (k,)
455 getattr(self, 'set_%s' % type)(k, v)
456
457 - def xlabel(self, s=None, offset=None, font=None):
458 """Set the plot's xlabel."""
459
460 self.set_label('xlabel', s, offset=offset, font=font)
461
462 - def ylabel(self, s=None, offset=None, font=None):
463 """Set the plot's ylabel."""
464
465 self.set_label('ylabel', s, offset=offset, font=font)
466
467 - def zlabel(self, s=None, offset=None, font=None):
468 """Set the plot's zlabel."""
469
470 self.set_label('zlabel', s, offset=offset, font=font)
471
472 - def title(self, s=None, offset=None, font=None):
473 """Set the plot's title."""
474
475 self.set_label('title', s, offset=offset, font=font)
476
477 - def hardcopy(self, filename=None, terminal='postscript', **keyw):
478 """Create a hardcopy of the current plot.
479
480 Output the current plot to the default printer (if configured)
481 or to the specified filename.
482
483 Note that gnuplot remembers the printer suboptions across
484 terminal changes (at least for postscript). Therefore if you
485 set, for example, color=1 for one hardcopy then the next
486 hardcopy will also be color unless you explicitly choose
487 color=0. Alternately you can force all of the options to
488 their defaults by setting mode='default'. I consider this to
489 be a bug in gnuplot.
490
491 Keyword arguments:
492
493 'filename=<string>' -- if a filename is specified, save the
494 output in that file; otherwise print it immediately
495 using the 'default_lpr' configuration option.
496
497 'terminal=<string>' -- the type of gnuplot 'terminal' to use
498 for the output (e.g., 'postscript', 'png', 'latex',
499 etc). Look in termdefs.py to see what terminal types
500 are defined, or check termdefs.terminal_opts.keys().
501
502 The rest of the keyword arguments depend on the terminal type.
503
504 Keyword arguments for 'postscript' terminal:
505
506 'mode=<string>' -- set the postscript submode ('landscape',
507 'portrait', 'eps', or 'default'). The default is
508 to leave this option unspecified.
509
510 'eps=<bool>' -- shorthand for 'mode="eps"'; asks gnuplot to
511 generate encapsulated postscript.
512
513 'enhanced=<bool>' -- if set (the default), then generate
514 enhanced postscript, which allows extra features like
515 font-switching, superscripts, and subscripts in axis
516 labels. (Some old gnuplot versions do not support
517 enhanced postscript; if this is the case set
518 gp.GnuplotOpts.prefer_enhanced_postscript=None.)
519
520 'color=<bool>' -- if set, create a plot with color. Default
521 is to leave this option unchanged.
522
523 'solid=<bool>' -- if set, force lines to be solid (i.e., not
524 dashed).
525
526 'duplexing=<string>' -- set duplexing option ('defaultplex',
527 'simplex', or 'duplex'). Only request double-sided
528 printing if your printer can handle it. Actually this
529 option is probably meaningless since hardcopy() can only
530 print a single plot at a time.
531
532 'fontname=<string>' -- set the default font to <string>,
533 which must be a valid postscript font. The default is
534 to leave this option unspecified.
535
536 'fontsize=<double>' -- set the default font size, in
537 postscript points.
538
539 Note that this command will return immediately even though it
540 might take gnuplot a while to actually finish working. Be
541 sure to pause briefly before issuing another command that
542 might cause the temporary files to be deleted.
543
544 """
545
546 if filename is None:
547 if gp.GnuplotOpts.default_lpr is None:
548 raise Errors.OptionError(
549 'default_lpr is not set, so you can only print to a file.')
550 filename = gp.GnuplotOpts.default_lpr
551
552
553
554
555
556
557
558
559
560
561 setterm = ['set', 'terminal', terminal]
562 try:
563 opts = termdefs.terminal_opts[terminal]
564 except KeyError:
565 raise Errors.OptionError(
566 'Terminal "%s" is not configured in Gnuplot.py.' % (terminal,))
567
568 for opt in opts:
569 cmd = opt(keyw)
570 if cmd is not None:
571 setterm.extend(cmd)
572 if keyw:
573
574 raise Errors.OptionError(
575 'The following options are unrecognized: %s'
576 % (", ".join(list(keyw.keys())),)
577 )
578
579 self.set_string('output', filename)
580 self(" ".join(setterm))
581
582 self.refresh()
583
584 self('set terminal %s' % gp.GnuplotOpts.default_term)
585 self.set_string('output')
586
587
588