Package lib :: Package plotting :: Module grace
[hide private]
[frames] | no frames]

Source Code for Module lib.plotting.grace

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2001-2005,2007-2010,2012-2015,2017 Edward d'Auvergne          # 
  4  # Copyright (C) 2008 Sebastien Morin                                          # 
  5  # Copyright (C) 2013 Troels E. Linnet                                         # 
  6  #                                                                             # 
  7  # This file is part of the program relax (http://www.nmr-relax.com).          # 
  8  #                                                                             # 
  9  # This program is free software: you can redistribute it and/or modify        # 
 10  # it under the terms of the GNU General Public License as published by        # 
 11  # the Free Software Foundation, either version 3 of the License, or           # 
 12  # (at your option) any later version.                                         # 
 13  #                                                                             # 
 14  # This program is distributed in the hope that it will be useful,             # 
 15  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 16  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 17  # GNU General Public License for more details.                                # 
 18  #                                                                             # 
 19  # You should have received a copy of the GNU General Public License           # 
 20  # along with this program.  If not, see <http://www.gnu.org/licenses/>.       # 
 21  #                                                                             # 
 22  ############################################################################### 
 23   
 24  # Module docstring. 
 25  """Module for interfacing with Grace (also known as Xmgrace, Xmgr, and ace).""" 
 26   
 27  # Python module imports. 
 28  from math import ceil, sqrt 
 29  from os import chmod 
 30  from os.path import expanduser 
 31  from stat import S_IRWXU, S_IRGRP, S_IROTH 
 32   
 33  # relax module imports. 
 34  from lib.errors import RelaxError 
 35  from lib.io import get_file_path, open_write_file 
 36   
 37   
 38  # This script is used to batch convert the Grace *.agr files into graphics files using the Grace 
 39  # program itself. 
 40  GRACE2IMAGES = """\ 
 41  #!/usr/bin/env python3 
 42   
 43  ############################################################################### 
 44  #                                                                             # 
 45  # Copyright (C) 2013 Troels E. Linnet                                         # 
 46  # Copyright (C) 2013,2017 Edward d'Auvergne                                   # 
 47  #                                                                             # 
 48  # This file is part of the program relax (http://www.nmr-relax.com).          # 
 49  #                                                                             # 
 50  # This program is free software: you can redistribute it and/or modify        # 
 51  # it under the terms of the GNU General Public License as published by        # 
 52  # the Free Software Foundation, either version 3 of the License, or           # 
 53  # (at your option) any later version.                                         # 
 54  #                                                                             # 
 55  # This program is distributed in the hope that it will be useful,             # 
 56  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 57  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 58  # GNU General Public License for more details.                                # 
 59  #                                                                             # 
 60  # You should have received a copy of the GNU General Public License           # 
 61  # along with this program.  If not, see <http://www.gnu.org/licenses/>.       # 
 62  #                                                                             # 
 63  ############################################################################### 
 64   
 65  # Script docstring. 
 66  \"\"\"Scripted conversion of Grace *.agr graphs into vector or bitmap graphics files. 
 67   
 68  This script is used to batch convert the Grace *.agr files into graphics bitmap files using the Grace program itself.  Therefore you will need to install grace on your system using one of the programs: 
 69      Xmgrace - http://plasma-gate.weizmann.ac.il/Grace/, 
 70      qtgrace - http://sourceforge.net/projects/qtgrace/, 
 71      gracegtk - http://sourceforge.net/projects/gracegtk/. 
 72  \"\"\" 
 73   
 74  # Python module imports. 
 75  from argparse import Action, ArgumentParser 
 76  import sys 
 77  from os import getcwd, listdir, path 
 78  import shlex, subprocess 
 79   
 80   
 81  # Define a callback class for handling the multiple input of PNG, EPS, SVG, etc. 
 82  class SplitFormats(Action): 
 83       def __call__(self, parser, namespace, values, option_string=None): 
 84           setattr(namespace, self.dest, values.split(',')) 
 85   
 86   
 87  # Add script argument parsing. 
 88  parser = ArgumentParser(description="Scripted conversion of Grace *.agr graphs into vector or bitmap graphics files.") 
 89   
 90  # Add the script arguments. 
 91  parser.add_argument('types', action=SplitFormats, nargs='?', default='EPS', help="The different image types to convert to.  E.g. execute script with:  python %s PNG,EPS,SVG,JPG" % (sys.argv[0])) 
 92  parser.add_argument('-g', action='store_true', dest='relax_gui', default=False, help="Allow the script to be run through the relax GUI via the 'User-functions -> script' submenu, by only allowing for PNG conversions.") 
 93   
 94  # Parse the arguments. 
 95  args = parser.parse_args() 
 96   
 97  # If we run through the GUI we cannot pass input arguments so we fall back to EPS-only conversion. 
 98  if args.relax_gui: 
 99      args.types = ['EPS'] 
100   
101  # For PNG conversion, several parameters can be passed to xmgrace.  These can be altered later and the script rerun. 
102  # The option for transparency is good for poster or insertion in color backgrounds.  This ability depends on the Grace compilation. 
103  if "PNG" in args.types: 
104      pngpar = "png.par" 
105      if not path.isfile(pngpar): 
106          wpngpar = open(pngpar, "w") 
107          wpngpar.write("DEVICE \\\"PNG\\\" FONT ANTIALIASING on\\n") 
108          wpngpar.write("DEVICE \\\"PNG\\\" OP \\\"transparent:off\\\"\\n") 
109          wpngpar.write("DEVICE \\\"PNG\\\" OP \\\"compression:9\\\"\\n") 
110          wpngpar.close() 
111   
112  # Convert the different possible graphics type options into Grace format. 
113  types = [] 
114  text = [] 
115  ext = [] 
116  param = [] 
117  for type in args.types: 
118      # PNG bitmap graphics. 
119      if type in ["PNG", ".PNG", "png", ".png"]: 
120          types.append("PNG") 
121          text.append("portable network graphics (PNG)") 
122          ext.append("png") 
123          param.append(pngpar) 
124   
125      # Encapsulated postscript vector graphics. 
126      elif type in ["EPS", ".EPS", "eps", ".eps"]: 
127          types.append("EPS") 
128          text.append("encapsulated postscript (EPS)") 
129          ext.append("eps") 
130          param.append(None) 
131   
132      # JPG bitmap graphics. 
133      elif type in ["JPG", ".JPG", "jpg", ".jpg", "JPEG", ".JPEG", "jpeg", ".jpeg"]: 
134          types.append("JPEG") 
135          text.append("JPEG") 
136          ext.append("jpg") 
137          param.append(None) 
138   
139      # Scalable vector graphics. 
140      elif type in ["SVG", ".SVG", "svg", ".svg"]: 
141          types.append("SVG") 
142          text.append("scalable vector graphics (SVG)") 
143          ext.append("svg") 
144          param.append(None) 
145   
146      # Unknown graphic. 
147      else: 
148          print("Unknown graphic type '%s', skipping the format." % type) 
149          continue 
150   
151  # Loop over all files in the current directory. 
152  for filename in listdir(getcwd()): 
153      # Skip non-Grace files. 
154      if not filename.endswith(".agr"): 
155          continue 
156   
157      # Get the filename without extension. 
158      basename = path.splitext(filename)[0] 
159   
160      # Loop over each output format. 
161      for i in range(len(types)): 
162          im_args = r"xmgrace -hdevice %s -hardcopy" % types[i] 
163          if param[i]: 
164              im_args += r" -param %s" % param[i] 
165          im_args += r" -printfile %s.%s %s" % (basename, ext[i], filename) 
166   
167          # Split the arguments the right way to call xmgrace. 
168          im_args = shlex.split(im_args) 
169   
170          # Generate the graphic. 
171          print("Converting '%s' into a %s graphic." % (filename, text[i])) 
172          return_code = subprocess.call(im_args) 
173  """ 
174   
175   
176 -def create_grace2images(dir=None):
177 """Create the grace2images.py executable script. 178 179 @keyword dir: The directory to place the script into. 180 @type dir: str 181 """ 182 183 # Expand any ~ characters. 184 dir = expanduser(dir) 185 186 # Open the file. 187 print("\nCreating the Python \"grace to PNG/EPS/SVG...\" conversion script.") 188 file_name = "grace2images.py" 189 file_path = get_file_path(file_name=file_name, dir=dir) 190 file = open_write_file(file_name=file_name, dir=dir, force=True) 191 192 # Write the Python "grace to PNG/EPS/SVG..." conversion script. 193 script_grace2images(file=file) 194 195 # Close the batch script, then make it executable (expanding any ~ characters). 196 file.close() 197 chmod(file_path, S_IRWXU|S_IRGRP|S_IROTH)
198 199
200 -def script_grace2images(file=None):
201 """Write a python "grace to PNG/EPS/SVG..." conversion script.. 202 203 The makes a conversion script to image types as PNG/EPS/SVG. The conversion is looping over a directory list of *.agr files, and making function calls to xmgrace. Successful conversion of images depends on the compilation of xmgrace. The input is a list of image types which is wanted, f.ex: PNG EPS SVG. PNG is default. 204 205 @keyword file: The file object to write the data to. 206 @type file: file object 207 """ 208 209 # Write to file 210 file.write(GRACE2IMAGES)
211 212
213 -def write_xy_data(data, file=None, graph_type=None, norm_type='first', norm=None, autoscale=True):
214 """Write the data into the Grace xy-scatter plot. 215 216 The numerical data should be supplied as a 4 dimensional list or array object. The first dimension corresponds to the graphs, Gx. The second corresponds the sets of each graph, Sx. The third corresponds to the data series (i.e. each data point). The forth is a list of the information about each point, it is a list where the first element is the x value, the second is the y value, the third is the optional dx or dy error (either dx or dy dependent upon the graph_type arg), and the forth is the optional dy error when graph_type is xydxdy (the third position is then dx). 217 218 219 @param data: The 4D structure of numerical data to graph (see docstring). 220 @type data: list of lists of lists of float 221 @keyword file: The file object to write the data to. 222 @type file: file object 223 @keyword graph_type: The graph type which can be one of xy, xydy, xydx, or xydxdy. 224 @type graph_type: str 225 @keyword norm_type: The point to normalise to 1. This can be 'first' or 'last'. 226 @type norm_type: str 227 @keyword norm: The normalisation flag which if set to True will cause all graphs to be normalised to 1. The first dimension is the graph. 228 @type norm: None or list of bool 229 @keyword autoscale: A flag which if True will cause the world view of each graph to be autoscaled (by placing the Grace command "@autoscale" at the end of the file). If you have supplied a world view for the header or the tick spacing, this argument should be set to False to prevent that world view from being overwritten. 230 @type autoscale: bool 231 """ 232 233 # Graph number. 234 graph_num = len(data) 235 236 # Defaults. 237 if not norm: 238 norm = [] 239 for gi in range(graph_num): 240 norm.append(False) 241 242 # Comment columns. 243 comment_col = 2 244 if graph_type in ['xydx', 'xydy']: 245 comment_col = 3 246 elif graph_type == 'xydxdy': 247 comment_col = 4 248 249 # Loop over the graphs. 250 for gi in range(graph_num): 251 # Loop over the data sets of the graph. 252 for si in range(len(data[gi])): 253 # The target and type. 254 file.write("@target G%s.S%s\n" % (gi, si)) 255 file.write("@type %s\n" % graph_type) 256 257 # Catch missing data sets. 258 if len(data[gi][si]) == 0: 259 file.write("&\n") 260 continue 261 262 # Normalisation (to the first data point y value!). 263 norm_fact = 1.0 264 if norm[gi]: 265 if norm_type == 'first': 266 norm_fact = data[gi][si][0][1] 267 elif norm_type == 'last': 268 norm_fact = data[gi][si][-1][1] 269 else: 270 raise RelaxError("The normalisation type '%s' must be one of ['first', 'last']." % norm_fact) 271 272 # Loop over the data points. 273 for point in data[gi][si]: 274 # Bad data. 275 if point[0] == None or point[1] == None: 276 continue 277 278 # X and Y data. 279 file.write("%30.15f %30.15f" % (point[0], point[1]/norm_fact)) 280 281 # The dx and dy errors. 282 if graph_type in ['xydx', 'xydy', 'xydxdy']: 283 # Catch if graph is a mix with and without error. 284 if len(point) < 3: 285 error = None 286 else: 287 error = point[2] 288 # Catch x or y-axis errors of None. 289 if error == None: 290 error = 0.0 291 292 # Write the error. 293 file.write(" %30.15f" % (error/norm_fact)) 294 295 # The dy errors of xydxdy. 296 if graph_type == 'xydxdy': 297 # Catch y-axis errors of None. 298 error = point[3] 299 if error == None: 300 error = 0.0 301 302 # Write the error. 303 file.write(" %30.15f" % (error/norm_fact)) 304 305 # The comment if given. 306 try: 307 file.write(" \"%s\"" % point[comment_col]) 308 except IndexError: 309 pass 310 311 # End the point. 312 file.write("\n") 313 314 # End of the data set i. 315 file.write("&\n") 316 317 # Autoscaling of all graphs to avoid user confusion. 318 if autoscale: 319 for i in range(graph_num): 320 file.write("@with g%i\n" % i) 321 file.write("@autoscale\n") 322 323 # Auto-arrange the graphs if multiple are present. 324 if len(data) > 1: 325 row_num = int(round(sqrt(len(data)))) 326 col_num = int(ceil(sqrt(len(data)))) 327 file.write("@arrange(%i, %i, .1, .1, .1, OFF, OFF, OFF)\n" % (row_num, col_num))
328 329
330 -def write_xy_header(file=None, paper_size='A4', title=None, subtitle=None, world=None, view=None, graph_num=1, sets=None, set_names=None, set_colours=None, x_axis_type_zero=None, y_axis_type_zero=None, symbols=None, symbol_sizes=None, symbol_fill=None, linestyle=None, linetype=None, linewidth=None, data_type=None, seq_type=None, axis_labels=None, tick_major_spacing=None, tick_minor_count=None, legend=None, legend_pos=None, legend_box_fill_pattern=None, legend_char_size=None, norm=None):
331 """Write the grace header for xy-scatter plots. 332 333 Many of these keyword arguments should be supplied in a [X, Y] list format, where the first element corresponds to the X data, and the second the Y data. Defaults will be used for any non-supplied args (or lists with elements set to None). 334 335 336 @keyword file: The file object to write the data to. 337 @type file: file object 338 @keyword paper_size: The paper size, i.e. 'A4'. If set to None, this will default to letter size. 339 @type paper_size: str 340 @keyword title: The title of the graph. 341 @type title: None or str 342 @keyword subtitle: The sub-title of the graph. 343 @type subtitle: None or str 344 @keyword world: The Grace plot default zoom. This consists of a list of the X-axis minimum, Y-axis minimum, X-axis maximum, and Y-axis maximum values. Each graph should supply its own world view. 345 @type world: Nor or list of list of numbers 346 @keyword view: List of 4 coordinates defining the graph view port. 347 @type view: None or list of float 348 @keyword graph_num: The total number of graphs. 349 @type graph_num: int 350 @keyword sets: The number of data sets in each graph. 351 @type sets: list of int 352 @keyword set_names: The names associated with each graph data set Gx.Sy. For example this can be a list of spin identification strings. The first dimension is the graph, the second is the set. 353 @type set_names: None or list of list of str 354 @keyword set_colours: The colours for each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 355 @type set_colours: None or list of list of int 356 @keyword x_axis_type_zero: The flags specifying if the X-axis should be placed at zero. 357 @type x_axis_type_zero: None or list of lists of bool 358 @keyword y_axis_type_zero: The flags specifying if the Y-axis should be placed at zero. 359 @type y_axis_type_zero: None or list of lists of bool 360 @keyword symbols: The symbol style for each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 361 @type symbols: None or list of list of int 362 @keyword symbol_sizes: The symbol size for each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 363 @type symbol_sizes: None or list of list of int 364 @keyword symbol_fill: The symbol file style for each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 365 @type symbol_fill: None or list of list of int 366 @keyword linestyle: The line style for each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 367 @type linestyle: None or list of list of int 368 @keyword linetype: The line type for each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 369 @type linetype: None or list of list of int 370 @keyword linewidth: The line width for all elements of each graph data set Gx.Sy. The first dimension is the graph, the second is the set. 371 @type linewidth: None or list of float 372 @keyword data_type: The axis data category (in the [X, Y] list format). 373 @type data_type: None or list of list of str 374 @keyword seq_type: The sequence data type (in the [X, Y] list format). This is for molecular sequence specific data and can be one of 'res', 'spin', or 'mixed'. 375 @type seq_type: None or list of list of str 376 @keyword tick_major_spacing: The spacing between major ticks. This is in the [X, Y] list format whereby the first dimension corresponds to the graph number. 377 @type tick_major_spacing: None or list of list of numbers 378 @keyword tick_minor_count: The number of minor ticks between the major ticks. This is in the [X, Y] list format whereby the first dimension corresponds to the graph number. 379 @type tick_minor_count: None or list of list of int 380 @keyword axis_labels: The labels for the axes (in the [X, Y] list format). The first dimension is the graph. 381 @type axis_labels: None or list of list of str 382 @keyword legend: If True, the legend will be visible. The first dimension is the graph. 383 @type legend: list of bool 384 @keyword legend_pos: The position of the legend, e.g. [0.3, 0.8]. The first dimension is the graph. 385 @type legend_pos: None or list of list of float 386 @keyword legend_box_fill_pattern: The legend box fill. If set to 0, it will become transparent. 387 @type legend_box_fill_pattern: int 388 @keyword legend_char_size: The size of the legend box text. 389 @type legend_char_size: float 390 @keyword norm: The normalisation flag which if set to True will cause all graphs to be normalised to 1. The first dimension is the graph. 391 @type norm: list of bool 392 """ 393 394 # Defaults. 395 if sets == None: 396 sets = [] 397 for gi in range(graph_num): 398 sets.append(1) 399 if x_axis_type_zero == None: 400 x_axis_type_zero = [] 401 for gi in range(graph_num): 402 x_axis_type_zero.append(False) 403 if y_axis_type_zero == None: 404 y_axis_type_zero = [] 405 for gi in range(graph_num): 406 y_axis_type_zero.append(False) 407 if linewidth == None: 408 linewidth = [] 409 for gi in range(graph_num): 410 linewidth.append(0.5) 411 if norm == None: 412 norm = [] 413 for gi in range(graph_num): 414 norm.append(False) 415 if legend == None: 416 legend = [] 417 for gi in range(graph_num): 418 legend.append(True) 419 if not legend_box_fill_pattern: 420 legend_box_fill_pattern = [] 421 for gi in range(graph_num): 422 legend_box_fill_pattern.append(1) 423 if not legend_char_size: 424 legend_char_size = [] 425 for gi in range(graph_num): 426 legend_char_size.append(1.0) 427 428 # Set the None args to lists as needed. 429 if not data_type: 430 data_type = [None, None] 431 if not seq_type: 432 seq_type = [None, None] 433 if not axis_labels: 434 axis_labels = [] 435 for gi in range(graph_num): 436 axis_labels.append([None, None]) 437 438 # Set the Grace version number of the header's formatting for compatibility. 439 file.write("@version 50121\n") 440 441 # The paper size. 442 if paper_size == 'A4': 443 file.write("@page size 842, 595\n") 444 445 # Loop over each graph. 446 for gi in range(graph_num): 447 # Graph Gi. 448 file.write("@with g%i\n" % gi) 449 450 # The world view. 451 if world: 452 file.write("@ world %s, %s, %s, %s\n" % (world[gi][0], world[gi][1], world[gi][2], world[gi][3])) 453 454 # The view port. 455 if not view: 456 view = [0.15, 0.15, 1.28, 0.85] 457 file.write("@ view %s, %s, %s, %s\n" % (view[0], view[1], view[2], view[3])) 458 459 # The title and subtitle. 460 if title: 461 file.write("@ title \"%s\"\n" % title) 462 if subtitle: 463 file.write("@ subtitle \"%s\"\n" % subtitle) 464 465 # Axis at zero. 466 if x_axis_type_zero[gi]: 467 file.write("@ xaxis type zero true\n") 468 if y_axis_type_zero[gi]: 469 file.write("@ yaxis type zero true\n") 470 471 # Axis specific settings. 472 axes = ['x', 'y'] 473 for i in range(2): 474 # Some axis default values for spin data. 475 if data_type[i] == 'spin': 476 # Residue only data. 477 if seq_type[i] == 'res': 478 # X-axis label. 479 if not axis_labels[gi][i]: 480 axis_labels[gi][i] = "Residue number" 481 482 # Spin only data. 483 if seq_type[i] == 'spin': 484 # X-axis label. 485 if not axis_labels[gi][i]: 486 axis_labels[gi][i] = "Spin number" 487 488 # Mixed data. 489 if seq_type[i] == 'mixed': 490 # X-axis label. 491 if not axis_labels[gi][i]: 492 axis_labels[gi][i] = "Spin ID string" 493 494 # Write out the data. 495 if axis_labels[gi][i]: 496 file.write("@ %saxis label \"%s\"\n" % (axes[i], axis_labels[gi][i])) 497 file.write("@ %saxis label char size 1.00\n" % axes[i]) 498 if tick_major_spacing != None: 499 file.write("@ %saxis tick major %s\n" % (axes[i], tick_major_spacing[gi][i])) 500 file.write("@ %saxis tick major size 0.50\n" % axes[i]) 501 file.write("@ %saxis tick major linewidth %s\n" % (axes[i], linewidth[gi])) 502 if tick_minor_count != None: 503 file.write("@ %saxis tick minor ticks %s\n" % (axes[i], tick_minor_count[gi][i])) 504 file.write("@ %saxis tick minor linewidth %s\n" % (axes[i], linewidth[gi])) 505 file.write("@ %saxis tick minor size 0.25\n" % axes[i]) 506 file.write("@ %saxis ticklabel char size 0.70\n" % axes[i]) 507 508 # Legend box. 509 if legend != None and legend[gi]: 510 file.write("@ legend on\n") 511 else: 512 file.write("@ legend off\n") 513 if legend_pos != None: 514 file.write("@ legend %s, %s\n" % (legend_pos[gi][0], legend_pos[gi][1])) 515 file.write("@ legend box fill pattern %s\n" % legend_box_fill_pattern[gi]) 516 file.write("@ legend char size %s\n" % legend_char_size[gi]) 517 518 # Frame. 519 file.write("@ frame linewidth %s\n" % linewidth[gi]) 520 521 # Loop over each graph set. 522 for i in range(sets[gi]): 523 # Symbol style (default to all different symbols). 524 if symbols: 525 file.write("@ s%i symbol %i\n" % (i, symbols[gi][i])) 526 else: 527 # The symbol number (cycle between 1 and 10). 528 num = i % 10 + 1 529 530 # Write out. 531 file.write("@ s%i symbol %i\n" % (i, num)) 532 533 # Symbol sizes (default to a small size). 534 if symbol_sizes: 535 file.write("@ s%i symbol size %s\n" % (i, symbol_sizes[gi][i])) 536 else: 537 file.write("@ s%i symbol size 0.45\n" % i) 538 539 # The symbol fill. 540 if symbol_fill: 541 file.write("@ s%i symbol fill pattern %i\n" % (i, symbol_fill[gi][i])) 542 543 # The symbol line width. 544 file.write("@ s%i symbol linewidth %s\n" % (i, linewidth[gi])) 545 546 # Symbol colour (default to nothing). 547 if set_colours: 548 file.write("@ s%i symbol color %s\n" % (i, set_colours[gi][i])) 549 file.write("@ s%i symbol fill color %s\n" % (i, set_colours[gi][i])) 550 551 # Error bars. 552 file.write("@ s%i errorbar size 0.5\n" % i) 553 file.write("@ s%i errorbar linewidth %s\n" % (i, linewidth[gi])) 554 file.write("@ s%i errorbar riser linewidth %s\n" % (i, linewidth[gi])) 555 556 # Line linestyle (default to nothing). 557 if linestyle: 558 file.write("@ s%i line linestyle %s\n" % (i, linestyle[gi][i])) 559 560 # Line linetype (default to nothing). 561 if linetype: 562 file.write("@ s%i line type %s\n" % (i, linetype[gi][i])) 563 564 # Line and all other colours (default to nothing). 565 if set_colours: 566 file.write("@ s%i line color %s\n" % (i, set_colours[gi][i])) 567 file.write("@ s%i fill color %s\n" % (i, set_colours[gi][i])) 568 file.write("@ s%i avalue color %s\n" % (i, set_colours[gi][i])) 569 file.write("@ s%i errorbar color %s\n" % (i, set_colours[gi][i])) 570 571 # Legend. 572 if set_names and len(set_names) and len(set_names[gi]) and set_names[gi][i]: 573 file.write("@ s%i legend \"%s\"\n" % (i, set_names[gi][i]))
574