-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGnuPlot.py
163 lines (146 loc) · 5.28 KB
/
GnuPlot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#!/usr/bin/python3
#
# GnuPlot - Simple abstraction on top of GnuPlot
# Copyright (C) 2020-2020 Johannes Bauer
#
# This file is part of pycommon.
#
# pycommon is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; this program is ONLY licensed under
# version 3 of the License, later versions are explicitly excluded.
#
# pycommon is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pycommon; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Johannes Bauer <JohannesBauer@gmx.de>
#
# File UUID ca1d0fde-959d-4250-857b-1fa64ea1c54d
import subprocess
def _escape(text):
return text
class GnuPlotDataset():
def __init__(self, xydata, title = "Graph", plotstyle = "lines", line_width = None, point_size = None, color = None, smooth = None, normalize_y = False, scale_y = 1, axis = 1):
assert((color is None) or (isinstance(color, str) and (len(color) == 6)))
assert(plotstyle in [ "lines", "dots", "points", "steps" ])
assert((smooth is None) or (smooth in [ "cspline", "bezier" ]))
assert(axis in [ 1, 2 ])
if isinstance(xydata, dict):
self._xydata = sorted(xydata.items())
else:
self._xydata = xydata
if normalize_y:
self._xydata = list(self._normalize(self._xydata))
self._title = title
self._plotstyle = plotstyle
self._line_width = line_width
self._point_size = point_size
self._color = color
self._default_color = None
self._smooth = smooth
self._scale_y = scale_y
self._axis = axis
@staticmethod
def _normalize(xydata):
ysum = 0
for (x, y) in xydata:
ysum += y
if ysum > 0:
for (x, y) in xydata:
yield (x, y / ysum)
def assign_default_color(self, color):
self._default_color = color
@property
def plot_cmd(self):
cmd = [ "\"-\" using 1:(%.6e*$2)" % (self._scale_y) ]
if self._plotstyle is not None:
cmd += [ "with", self._plotstyle ]
if self._title is not None:
cmd += [ "title", "\"%s\"" % (_escape(self._title)) ]
if self._line_width is not None:
cmd += [ "linewidth", "%.2f" % (self._line_width) ]
if self._point_size is not None:
cmd += [ "pointsize", "%.2f" % (self._point_size) ]
if self._color is not None:
cmd += [ "linetype", "rgb", "\"#%s\"" % (self._color) ]
elif self._default_color is not None:
cmd += [ "linetype", "rgb", "\"#%s\"" % (self._default_color) ]
if self._smooth is not None:
cmd += [ "smooth", self._smooth ]
if self._axis == 2:
cmd += [ "axis", "x1y2" ]
return " ".join(cmd)
def __iter__(self):
return iter(self._xydata)
class GnuPlotDiagram():
_DEFAULT_COLORS = [
"2980b9",
"27ae60",
"c0392b",
"2c3e50",
"8e44ad",
]
def __init__(self, terminal = "pngcairo", default_width = 1920, default_height = 1080, title = None, xtitle = None, ytitle = None, ytitle2 = None):
self._terminal = terminal
self._default_width = default_width
self._default_height = default_height
self._title = title
self._xtitle = xtitle
self._ytitle = ytitle
self._ytitle2 = ytitle2
self._datasets = [ ]
self._x_timelabel = None
def make_timeplot(self, xlabel = "%H:%M:%S"):
self._x_timelabel = xlabel
return self
def add_dataset(self, dataset):
assert(isinstance(dataset, GnuPlotDataset))
self._datasets.append(dataset)
return self
def _assign_default_colors(self):
for (index, dataset) in enumerate(self._datasets):
color = self._DEFAULT_COLORS[index % len(self._DEFAULT_COLORS)]
dataset.assign_default_color(color)
def gnuplot_source(self):
self._assign_default_colors()
yield "set terminal %s size %d,%d" % (self._terminal, self._default_width, self._default_height)
if self._title is not None:
yield "set title \"%s\"" % (_escape(self._title))
if self._xtitle is not None:
yield "set xlabel \"%s\"" % (_escape(self._xtitle))
if self._ytitle is not None:
yield "set ylabel \"%s\"" % (_escape(self._ytitle))
if self._ytitle2 is not None:
yield "set y2label \"%s\"" % (_escape(self._ytitle2))
if self._x_timelabel is not None:
yield "set xdata time"
yield "set timefmt \"%s\""
yield "set format x \"%s\"" % (self._x_timelabel)
yield "set ytics nomirror"
yield "set y2tics nomirror"
yield "plot %s" % (", ".join(dataset.plot_cmd for dataset in self._datasets))
yield ""
for dataset in self._datasets:
for (x, y) in dataset:
if self._x_timelabel is None:
yield "%.6e %.6e" % (x, y)
else:
yield "%.6f %.6e" % (x, y)
yield "e"
yield ""
def gnuplot_render(self):
source = "\n".join(line for line in self.gnuplot_source()) + "\n"
rendered_doc = subprocess.check_output([ "gnuplot" ], input = source.encode())
return rendered_doc
def write_rendered(self, filename):
with open(filename, "wb") as f:
f.write(self.gnuplot_render())
if __name__ == "__main__":
gpd = GnuPlotDiagram(title = "footitle", xtitle = "X axis", ytitle = "Y axis").add_dataset(GnuPlotDataset([ (0, 0), (1, 10), (2, 25), (3, 15), (4, 14) ], line_width = 3, color = "ff0000", plotstyle = "steps", smooth = "cspline"))
gpd.write_rendered("graph.png")