Skip to content

Instantly share code, notes, and snippets.

@pklaus
Last active August 5, 2020 18:07
Show Gist options
  • Save pklaus/9ef636cb9a80de2af4f7 to your computer and use it in GitHub Desktop.
Save pklaus/9ef636cb9a80de2af4f7 to your computer and use it in GitHub Desktop.
Plotting with gnuplot via Python
#!/usr/bin/env python
"""
This example shows how to call gnuplot from Python
It uses Popen() from the subprocess module.
The function run_gnuplot() takes care of it and returns the
resulting image as bytes. If you run this code on a web server,
the result could be returned to the client without storing
a file on the disk. In this example it is saved to a file in the end.
"""
from subprocess import Popen, PIPE
import sys
from os import linesep as nl
import logging
import itertools
import random
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.WARNING)
def run_gnuplot(commands, data, persist=True):
""" Return the output of a gnuplot command """
args = ["gnuplot", ]
if persist:
args.append("-persist")
args.append("-e")
args.append("; ".join([str(c) for c in commands]))
logger.info('args: {}'.format(args))
in_pipe, out_pipe, err_pipe = PIPE, PIPE, PIPE
gnuplot = Popen(args, stdin=in_pipe, stdout=out_pipe, stderr=err_pipe)
logger.info('data: {}'.format(nl.join(data).encode('ascii')))
out, err = gnuplot.communicate(nl.join(data).encode('ascii'))
logger.debug('out: {}'.format(out))
if err:
logger.error('err: {}'.format(err))
if err:
raise NameError('There was a problem with the gnuplot commands or data: ' + err.decode('ascii'))
if gnuplot.returncode != 0:
raise NameError('The returncode of gnuplot was not zero but {}. There was a problem with the gnuplot commands or data.'.format(gnuplot.returncode))
return out
def gnuplot_scope(all_data, context):
"""
all_data contains the list of data sets (list of lists)
context is a dictionary with contextual information for the plot
"""
commands = [
"set title '{title}'",
"set term {format} size {size}",
"set key left",
"set yrange [{ymin}:{ymax}]",
"set xrange [0:1200]",
"set xtics {tics}",
"set xlabel ' Time {timescale} {tu} per div'",
"set ylabel ' Voltage in V'",
"set grid",
"set object 1 rect from screen 0, 0, 0 to screen 1, 1, 0 behind",
"set object 1 rect fc rgb {bgcolor} fillstyle solid 1.0",
"set datafile separator ','",
"set output",
]
commands = [cmd.format(**context) for cmd in commands]
data_lines = []
i = 0
plot_commands = []
for chan_data in all_data:
logger.info('preparing {} - {}'.format(i, context['channels'][i]))
plot_commands.append("'{source}' using 1 with lines title '{name}'".format(source=('-' if i==0 else ''), name=context['channels'][i]))
data_lines += [str(val) for val in chan_data] + ['e']
i += 1
commands += ['plot' + ', '.join(str(c) for c in plot_commands)]
return (commands, data_lines)
if __name__ == "__main__":
context = {}
context['title'] = 'Your Scope'
context['channels'] = ['CHAN1', 'CHAN2']
context['format'] = 'png' # or 'svg'
context['size'] = '1000,640'
context['bgcolor'] = '"white"' # or '"gray"'
alldata = []
for channel in context['channels']:
values = itertools.accumulate([random.uniform(-0.05, 0.05) for i in range(1200)])
alldata.append(list(values))
min_value = min(min(samples) for samples in alldata)
max_value = max(max(samples) for samples in alldata)
context['ymin'] = min_value - 0.1 * (max_value - min_value)
context['ymax'] = max_value + 0.2 * (max_value - min_value)
context['tics'] = '(' + ', '.join(str(i*100) for i in range(1, 12)) + ')'
context['timescale'] = float('100e-6')
context['tu'] = 's'
(commands, data_lines) = gnuplot_scope(alldata, context)
gnuplot_output = run_gnuplot(commands, data_lines, persist=False)
filename = 'tmp.png'
with open(filename, 'wb') as f:
f.write(gnuplot_output)
print("Wrote {}!".format(filename))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment