import os class GnuplotDraw: line_colors = {'red': 1, 'green': 2, 'blue': 3, 'cyan': 5, 'magenta': 4, 'aqua': 5, 'purple': 4, 'yellow': 7, 'black': -1} def __init__(self, xmin, xmax, ymin, ymax, axis=False, instruction_file=None): """ Define the drawing area [xmin,xmax]x[ymin,ymax]. axis: None or False means that axes with tickmarks are not drawn. instruction_file: name of file where all the instructions are recorded. """ self.xmin, self.xmax, self.ymin, self.ymax = \ float(xmin), float(xmax), float(ymin), float(ymax) self.axis = axis # Compute the right X11 geometry on the screen based on the # x-y ratio of axis ranges ratio = (self.ymax-self.ymin)/(self.xmax-self.xmin) self.xsize = 500 self.ysize = 500*ratio geometry = '%dx%d' % (self.xsize, self.ysize) self.g = os.popen('gnuplot -geometry %s -persist' % geometry, 'w') if instruction_file is not None: # Collect all commands in __call__ in a file self.instruction_file = open(instruction_file, 'w') else: self.instruction_file = None if not axis: self("unset xtics; unset ytics\n") self("set xrange[%g:%g]; set yrange[%s:%s]\n" % (xmin, xmax, ymin, ymax)) self("set size square") # equal aspect ratio self.erase() self.file_counter = 0 self.set_linecolor('red') self.set_linewidth(2) self.filled_curves(False) def set_linecolor(self, color): """Change the color of lines.""" self.linecolor = GnuplotDraw.line_colors[color] def set_linewidth(self, width): """Change the line width (int, starts at 1).""" self.linewidth = width def filled_curves(self, on=True): """Fill area inside curves with current line color.""" self.filledcurves = on def erase(self): """Erase the current figure.""" # Don't set self.counter=0 here because that will overwrite # older files being plotted by Gnuplot. self.plot_commands = [] def define_curve(self, x, y): """Define a curve with coordinates x and y (arrays).""" self.file_counter += 1 filename = '.tmp_%04d' % self.file_counter f = open(filename, 'w') for xi, yi in zip(x, y): f.write('%g %g\n' % (xi, yi)) f.close() if self.filledcurves: with_value = 'filledcurves' else: with_value = 'lines' self.plot_commands.append('"%s" with %s title "" lt %s lw %s' % (filename, with_value, self.linecolor, self.linewidth)) def display(self): """Display the figure.""" if not self.plot_commands: return # The set output command is important because if hardcopy is # called and output is set to a file, a new set term x11 will # overwrite the file with empty content (see the resulting # set of commands to realize how PNG and x11 plots are made # in sequence). plotcommand = 'set output; set term x11; plot ' + \ ', '.join(self.plot_commands) + '\n' self(plotcommand) #self('pause 2\n') def hardcopy(self, name): """Save figure in PNG file name.png.""" # Ratio is preserved by setting size 10cm,10cm for example # (at least this works for postscript) # Important: just running replot will not work if not display # is called so we make the full plot command here. plotcommand = 'plot ' + ', '.join(self.plot_commands) + '\n' self(""" set term png small size %d,%d set output "%s.png" %s """% (self.xsize, self.ysize, name, plotcommand)) def text(self, text, position, alignment='center', fontsize=18): """ Write text at a position (centered, left, right - according to the alignment string). position is a 2-tuple. """ self('set label "%s" at %s,%s %s font "Helvetica,%d" front; replot\n' % (text, position[0], position[1], alignment, fontsize)) def length_symbol(self, start, stop, symbol): #Divide arrow into two pieces, a textbox in the middle. #set arrow from 0,0 to 2,3 linewidth 1 heads #set label "..." at x,y center rotate by font "Times,18" front pass def __call__(self, command): if self.instruction_file is not None: self.instruction_file.write(command) self.g.write(command) self.g.flush()