Package generic_fns :: Module grace
[hide private]
[frames] | no frames]

Source Code for Module generic_fns.grace

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2003-2012 Edward d'Auvergne                                   # 
  4  #                                                                             # 
  5  # This file is part of the program relax (http://www.nmr-relax.com).          # 
  6  #                                                                             # 
  7  # This program is free software: you can redistribute it and/or modify        # 
  8  # it under the terms of the GNU General Public License as published by        # 
  9  # the Free Software Foundation, either version 3 of the License, or           # 
 10  # (at your option) any later version.                                         # 
 11  #                                                                             # 
 12  # This program is distributed in the hope that it will be useful,             # 
 13  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 15  # GNU General Public License for more details.                                # 
 16  #                                                                             # 
 17  # You should have received a copy of the GNU General Public License           # 
 18  # along with this program.  If not, see <http://www.gnu.org/licenses/>.       # 
 19  #                                                                             # 
 20  ############################################################################### 
 21   
 22  # Module docstring. 
 23  """Module for interfacing with Grace (also known as Xmgrace, Xmgr, and ace).""" 
 24   
 25  # Python module imports. 
 26  from numpy import array, ndarray 
 27  from os import system 
 28  from warnings import warn 
 29   
 30  # relax module imports. 
 31  import generic_fns 
 32  from generic_fns.mol_res_spin import count_molecules, count_residues, count_spins, exists_mol_res_spin_data, generate_spin_id, spin_loop 
 33  from generic_fns import pipes 
 34  from generic_fns.result_files import add_result_file 
 35  from relax_errors import RelaxError, RelaxNoSequenceError, RelaxNoSimError 
 36  from relax_io import get_file_path, open_write_file, test_binary 
 37  from relax_warnings import RelaxWarning 
 38  import specific_fns 
 39  from status import Status; status = Status() 
 40   
 41   
42 -def determine_seq_type(spin_id=None):
43 """Determine the spin sequence data type. 44 45 The purpose is to identify systems whereby only spins or only residues exist. 46 47 @keyword spin_id: The spin identification string. 48 @type spin_id: str 49 @return: The spin sequence data type. This can be one of 'spin', 'res,' or 'mixed'. 50 @rtype: str 51 """ 52 53 # Count the molecules, residues, and spins. 54 num_mol = count_molecules(spin_id) 55 num_res = count_residues(spin_id) 56 num_spin = count_spins(spin_id) 57 58 # Only residues. 59 if num_mol == 1 and num_spin == 1: 60 return 'res' 61 62 # Only spins. 63 if num_mol == 1 and num_res == 1: 64 return 'spin' 65 66 # Mixed. 67 return 'mixed'
68 69
70 -def get_data(spin_id=None, x_data_type=None, y_data_type=None, plot_data=None):
71 """Return all the xy data, along with the graph type and names for the graph sets. 72 73 @keyword spin_id: The spin identification string. 74 @type spin_id: str 75 @keyword x_data_type: The category of the X-axis data. 76 @type x_data_type: str 77 @keyword y_data_type: The category of the Y-axis data. 78 @type y_data_type: str 79 @keyword plot_data: The type of the plotted data, one of 'value', 'error', or 'sim'. 80 @type plot_data: str 81 @return: The 4D graph numerical data structure, the graph type (i.e. on of 'xy', 'xydy', or 'xydxdy'), and the labels for the graph sets. 82 @rtype: list of lists of lists of float, str, and list of str 83 """ 84 85 # Initialise the 4D data structure (Gx, Sx, data point, data point info), and graph set labels. 86 data = [[]] 87 set_labels = [] 88 x_err_flag = False 89 y_err_flag = False 90 data_list = False 91 data_dict = False 92 93 # Specific x and y value returning functions. 94 x_return_value = y_return_value = specific_fns.setup.get_specific_fn('return_value', pipes.get_type()) 95 x_return_conversion_factor = y_return_conversion_factor = specific_fns.setup.get_specific_fn('return_conversion_factor', pipes.get_type()) 96 97 # Test if the X-axis data type is a minimisation statistic. 98 if x_data_type != 'spin' and generic_fns.minimise.return_data_name(x_data_type): 99 x_return_value = generic_fns.minimise.return_value 100 x_return_conversion_factor = generic_fns.minimise.return_conversion_factor 101 102 # Test if the Y-axis data type is a minimisation statistic. 103 if y_data_type != 'spin' and generic_fns.minimise.return_data_name(y_data_type): 104 y_return_value = generic_fns.minimise.return_value 105 y_return_conversion_factor = generic_fns.minimise.return_conversion_factor 106 107 # Number of graph sets. 108 if plot_data == 'sim': 109 sets = cdp.sim_number 110 else: 111 sets = 1 112 113 # Loop over the data points. 114 for i in range(sets): 115 # The graph label. 116 set_label = '' 117 if plot_data == 'sim': 118 set_label = "Sim: %i" % i 119 120 # The sim number. 121 sim = None 122 if plot_data == 'sim': 123 sim = i 124 125 # Spin names list (for creating new graph sets). 126 spin_names = [] 127 128 # Loop over the spins. 129 for spin, mol_name, res_num, res_name, id in spin_loop(full_info=True, selection=spin_id, return_id=True): 130 # Skip deselected spins. 131 if not spin.select: 132 continue 133 134 # The X data (plotted as residue numbers). 135 if x_data_type == 'spin': 136 x_val = res_num 137 x_err = None 138 139 # The X data (plotted as values). 140 else: 141 # Append the x-axis values and errors. 142 x_val, x_err = x_return_value(spin, x_data_type, sim=sim) 143 144 # The Y data (plotted as residue numbers). 145 if y_data_type == 'spin': 146 y_val = res_num 147 y_err = None 148 149 # The Y data (plotted as values). 150 else: 151 # Append the y-axis values and errors. 152 y_val, y_err = y_return_value(spin, y_data_type, sim=sim) 153 154 # Go to the next spin if there is missing xy data. 155 if x_val == None or y_val == None: 156 continue 157 158 # One set per spin (list data has been returned). 159 if data_list or isinstance(x_val, list): 160 # Append a new set structure and set the name to the spin ID. 161 data[0].append([]) 162 set_labels.append("Spin %s" % id) 163 164 # The set index. 165 index = len(data[0]) - 1 166 167 # No errors. 168 if x_err == None: 169 x_err = [None]*len(x_val) 170 if y_err == None: 171 y_err = [None]*len(y_val) 172 173 # Data list flag. 174 data_list = True 175 176 # One set per spin (dictionary data has been returned). 177 if data_dict or isinstance(x_val, dict): 178 # Append a new set structure and set the name to the spin ID. 179 data[0].append([]) 180 set_labels.append("Spin %s" % id) 181 182 # The set index. 183 index = len(data[0]) - 1 184 185 # Convert to lists. 186 list_data = [] 187 for key in x_val.keys(): 188 list_data.append([x_val[key], y_val[key]]) 189 list_data.sort() 190 191 # Overwrite the data structures. 192 x_val = [] 193 y_val = [] 194 for i in range(len(list_data)): 195 x_val.append(list_data[i][0]) 196 y_val.append(list_data[i][1]) 197 198 # No errors. 199 if x_err == None: 200 x_err = [None]*len(x_val) 201 if y_err == None: 202 y_err = [None]*len(y_val) 203 204 # Data list flag. 205 data_dict = True 206 207 # Convert the data to lists for packing into 1 point. 208 else: 209 x_val = [x_val] 210 y_val = [y_val] 211 x_err = [x_err] 212 y_err = [y_err] 213 214 # A new spin type (one data set per spin type). 215 if not data_list and not data_dict: 216 if spin.name not in spin_names: 217 # Append a new set structure. 218 data[0].append([]) 219 220 # Append the label. 221 set_labels.append("%s spins. " % spin.name + set_label) 222 223 # Add the spin name to the list. 224 spin_names.append(spin.name) 225 226 # The set index. 227 index = i * len(spin_names) + spin_names.index(spin.name) 228 229 # Existing spin type, so change the index to match the correct data category (fix for bug #20120, https://web.archive.org/web/https://gna.org/bugs/?20120). 230 else: 231 index = spin_names.index(spin.name) 232 233 # Loop over the points. 234 for j in range(len(x_val)): 235 # Initialise and alias point structure. 236 data[0][index].append([]) 237 point = data[0][index][-1] 238 239 # Conversion factors. 240 if x_data_type != 'spin': 241 x_val[j] = x_val[j] / x_return_conversion_factor(x_data_type) 242 if x_err[j] and x_data_type != 'spin': 243 x_err[j] = x_err[j] / x_return_conversion_factor(x_data_type) 244 y_val[j] = y_val[j] / y_return_conversion_factor(y_data_type) 245 if y_err[j] and y_data_type != 'spin': 246 y_err[j] = y_err[j] / y_return_conversion_factor(y_data_type) 247 248 # Append the data. 249 point.append(x_val[j]) 250 point.append(y_val[j]) 251 point.append(x_err[j]) 252 point.append(y_err[j]) 253 254 # Error flags. 255 if x_err[j] and not x_err_flag: 256 x_err_flag = True 257 if y_err[j] and not y_err_flag: 258 y_err_flag = True 259 260 # The graph type. 261 graph_type = 'xy' 262 if x_err_flag: 263 graph_type = graph_type + 'dx' 264 if y_err_flag: 265 graph_type = graph_type + 'dy' 266 267 # Remodel the data. 268 new_data = [] 269 for i in range(len(data)): 270 new_data.append([]) 271 for j in range(len(data[i])): 272 new_data[i].append([]) 273 for k in range(len(data[i][j])): 274 # The xy data. 275 new_data[i][j].append([]) 276 new_data[i][j][k].append(data[i][j][k][0]) 277 new_data[i][j][k].append(data[i][j][k][1]) 278 279 # First error set. 280 if graph_type in ['xydx', 'xydxdy']: 281 new_data[i][j][k].append(data[i][j][k][2]) 282 283 # Second error set. 284 if graph_type in ['xydy', 'xydxdy']: 285 new_data[i][j][k].append(data[i][j][k][3]) 286 287 # Return the data. 288 return new_data, set_labels, graph_type
289 290
291 -def get_data_types():
292 """Get all of the data types to plot for the current data pipe. 293 294 @return: A list of lists of all the allowable data type descriptions and their values. 295 @rtype: list of list of str 296 """ 297 298 # Get the specific functions (return an empty list if a RelaxError occurs). 299 try: 300 data_names = specific_fns.setup.get_specific_fn('data_names', cdp.pipe_type, raise_error=False) 301 return_data_desc = specific_fns.setup.get_specific_fn('return_data_desc', cdp.pipe_type, raise_error=False) 302 except: 303 return [] 304 305 # The data names, if they exist. 306 names = data_names(set='params') 307 308 # Initialise the list and then add the sequence data. 309 data = [] 310 data.append(["Spin sequence", 'spin']) 311 312 # Loop over the parameters. 313 for name in (data_names(set='params') + data_names(set='generic')): 314 # Get the description. 315 try: 316 desc = return_data_desc(name) 317 except: 318 return [] 319 320 # No description. 321 if not desc: 322 text = name 323 324 # The text. 325 else: 326 text = "'%s': %s" % (name, desc) 327 328 # Append the description. 329 data.append([text, name]) 330 331 # Return the data. 332 return data
333 334
335 -def view(file=None, dir=None, grace_exe='xmgrace'):
336 """Execute Grace. 337 338 @keyword file: The name of the file to open in Grace. 339 @type file: str 340 @keyword dir: The optional directory containing the file. 341 @type dir: str 342 @keyword grace_exe: The name of the Grace executable file. This should be located within the 343 system path. 344 @type grace_exe: str 345 """ 346 347 # Test the binary file string corresponds to a valid executable. 348 test_binary(grace_exe) 349 350 # File path. 351 file_path = get_file_path(file, dir) 352 353 # Run Grace. 354 system(grace_exe + " " + file_path + " &")
355 356
357 -def write(x_data_type='spin', y_data_type=None, spin_id=None, plot_data='value', file=None, dir=None, force=False, norm=True):
358 """Writing data to a file. 359 360 @keyword x_data_type: The category of the X-axis data. 361 @type x_data_type: str 362 @keyword y_data_type: The category of the Y-axis data. 363 @type y_data_type: str 364 @keyword spin_id: The spin identification string. 365 @type spin_id: str 366 @keyword plot_data: The type of the plotted data, one of 'value', 'error', or 'sim'. 367 @type plot_data: str 368 @keyword file: The name of the Grace file to create. 369 @type file: str 370 @keyword dir: The optional directory to place the file into. 371 @type dir: str 372 @param force: Boolean argument which if True causes the file to be overwritten if it 373 already exists. 374 @type force: bool 375 @keyword norm: The normalisation flag which if set to True will cause all graphs to be 376 normalised to a starting value of 1. 377 @type norm: bool 378 """ 379 380 # Test if the current pipe exists. 381 pipes.test() 382 383 # Test if the sequence data is loaded. 384 if not exists_mol_res_spin_data(): 385 raise RelaxNoSequenceError 386 387 # Test if the plot_data argument is one of 'value', 'error', or 'sim'. 388 if plot_data not in ['value', 'error', 'sim']: 389 raise RelaxError("The plot data argument " + repr(plot_data) + " must be set to either 'value', 'error', 'sim'.") 390 391 # Test if the simulations exist. 392 if plot_data == 'sim' and not hasattr(cdp, 'sim_number'): 393 raise RelaxNoSimError 394 395 # Open the file for writing. 396 file_path = get_file_path(file, dir) 397 file = open_write_file(file, dir, force) 398 399 # Get the data. 400 data, set_names, graph_type = get_data(spin_id, x_data_type=x_data_type, y_data_type=y_data_type, plot_data=plot_data) 401 402 # No data, so close the empty file and exit. 403 if not len(data) or not len(data[0]) or not len(data[0][0]): 404 warn(RelaxWarning("No data could be found, creating an empty file.")) 405 file.close() 406 return 407 408 # Determine the sequence data type. 409 seq_type = [None, None] 410 if x_data_type == 'spin': 411 seq_type[0] = 'res' 412 if y_data_type == 'spin': 413 seq_type[1] = 'res' 414 415 # Write the header. 416 write_xy_header(sets=len(data[0]), file=file, data_type=[x_data_type, y_data_type], seq_type=seq_type, set_names=set_names, norm=norm) 417 418 # Write the data. 419 write_xy_data(data, file=file, graph_type=graph_type, norm=norm) 420 421 # Close the file. 422 file.close() 423 424 # Add the file to the results file list. 425 add_result_file(type='grace', label='Grace', file=file_path)
426 427
428 -def write_xy_data(data, file=None, graph_type=None, norm=False):
429 """Write the data into the Grace xy-scatter plot. 430 431 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). 432 433 434 @param data: The 4D structure of numerical data to graph (see docstring). 435 @type data: list of lists of lists of float 436 @keyword file: The file object to write the data to. 437 @type file: file object 438 @keyword graph_type: The graph type which can be one of xy, xydy, xydx, or xydxdy. 439 @type graph_type: str 440 @keyword norm: The normalisation flag which if set to True will cause all graphs to be normalised to 1. 441 @type norm: bool 442 """ 443 444 # Comment columns. 445 comment_col = 2 446 if graph_type in ['xydx', 'xydy']: 447 comment_col = 3 448 elif graph_type == 'xydxdy': 449 comment_col = 4 450 451 # Loop over the graphs. 452 for gi in range(len(data)): 453 # Loop over the data sets of the graph. 454 for si in range(len(data[gi])): 455 # The target and type. 456 file.write("@target G%s.S%s\n" % (gi, si)) 457 file.write("@type %s\n" % graph_type) 458 459 # Normalisation (to the first data point y value!). 460 norm_fact = 1.0 461 if norm: 462 norm_fact = data[gi][si][0][1] 463 464 # Loop over the data points. 465 for point in data[gi][si]: 466 # Bad data. 467 if point[0] == None or point[1] == None: 468 continue 469 470 # X and Y data. 471 file.write("%-30s %-30.15f" % (point[0], point[1]/norm_fact)) 472 473 # The dx and dy errors. 474 if graph_type in ['xydx', 'xydy', 'xydxdy']: 475 # Catch x or y-axis errors of None. 476 error = point[2] 477 if error == None: 478 error = 0.0 479 480 # Write the error. 481 file.write(" %-30.15f" % (error/norm_fact)) 482 483 # The dy errors of xydxdy. 484 if graph_type == 'xydxdy': 485 # Catch y-axis errors of None. 486 error = point[3] 487 if error == None: 488 error = 0.0 489 490 # Write the error. 491 file.write(" %-30.15f" % (error/norm_fact)) 492 493 # The comment if given. 494 try: 495 file.write("%30s \"# %s\"" % ('', point[comment_col])) 496 except IndexError: 497 pass 498 499 # End the point. 500 file.write("\n") 501 502 # End of the data set i. 503 file.write("&\n")
504 505
506 -def write_xy_header(file=None, paper_size='A4', title=None, subtitle=None, view=None, sets=1, set_names=None, set_colours=None, symbols=None, symbol_sizes=None, symbol_fill=None, linestyle=None, linetype=None, linewidth=0.5, data_type=None, seq_type=None, axis_labels=None, axis_min=None, axis_max=None, legend_pos=None, legend=False, norm=False):
507 """Write the grace header for xy-scatter plots. 508 509 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). 510 511 512 @keyword file: The file object to write the data to. 513 @type file: file object 514 @keyword paper_size: The paper size, i.e. 'A4'. If set to None, this will default to letter size. 515 @type paper_size: str 516 @keyword title: The title of the graph. 517 @type title: None or str 518 @keyword subtitle: The sub-title of the graph. 519 @type subtitle: None or str 520 @keyword view: List of 4 coordinates defining the graph view port. 521 @type view: None or list of float 522 @keyword sets: The number of data sets in the graph G0. 523 @type sets: int 524 @keyword set_names: The names associated with each graph data set G0.Sx. For example this can be a list of spin identification strings. 525 @type set_names: None or list of str 526 @keyword set_colours: The colours for each graph data set G0.Sx. 527 @type set_colours: None or list of int 528 @keyword symbols: The symbol style for each graph data set G0.Sx. 529 @type symbols: None or list of int 530 @keyword symbol_sizes: The symbol size for each graph data set G0.Sx. 531 @type symbol_sizes: None or list of int 532 @keyword symbol_fill: The symbol file style for each graph data set G0.Sx. 533 @type symbol_fill: None or list of int 534 @keyword linestyle: The line style for each graph data set G0.Sx. 535 @type linestyle: None or list of int 536 @keyword linetype: The line type for each graph data set G0.Sx. 537 @type linetype: None or list of int 538 @keyword linewidth: The line width for all elements of each graph data set G0.Sx. 539 @type linewidth: None or float 540 @keyword data_type: The axis data category (in the [X, Y] list format). 541 @type data_type: None or list of str 542 @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'. 543 @type seq_type: None or list of str 544 @keyword axis_labels: The labels for the axes (in the [X, Y] list format). 545 @type axis_labels: None or list of str 546 @keyword axis_min: The minimum values for specifying the graph ranges (in the [X, Y] list format). 547 @type axis_min: None or list of str 548 @keyword axis_max: The maximum values for specifying the graph ranges (in the [X, Y] list format). 549 @type axis_max: None or list of str 550 @keyword legend_pos: The position of the legend, e.g. [0.3, 0.8]. 551 @type legend_pos: None or list of float 552 @keyword legend: If True, the legend will be visible. 553 @type legend: bool 554 @keyword norm: The normalisation flag which if set to True will cause all graphs to be normalised to 1. 555 @type norm: bool 556 """ 557 558 # Set the None args to lists as needed. 559 if not data_type: 560 data_type = [None, None] 561 if not seq_type: 562 seq_type = [None, None] 563 if not axis_labels: 564 axis_labels = [None, None] 565 if not axis_min: 566 axis_min = [None, None] 567 if not axis_max: 568 axis_max = [None, None] 569 570 # Set the Grace version number of the header's formatting for compatibility. 571 file.write("@version 50121\n") 572 573 # The paper size. 574 if paper_size == 'A4': 575 file.write("@page size 842, 595\n") 576 577 # Graph G0. 578 file.write("@with g0\n") 579 580 # The view port. 581 if not view: 582 view = [0.15, 0.15, 1.28, 0.85] 583 file.write("@ view %s, %s, %s, %s\n" % (view[0], view[1], view[2], view[3])) 584 585 # The title and subtitle. 586 if title: 587 file.write("@ title \"%s\"\n" % title) 588 if subtitle: 589 file.write("@ subtitle \"%s\"\n" % subtitle) 590 591 # Axis specific settings. 592 axes = ['x', 'y'] 593 for i in range(2): 594 # Analysis specific methods for making labels. 595 analysis_spec = False 596 if pipes.cdp_name(): 597 # Flag for making labels. 598 analysis_spec = True 599 600 # Specific value and error, conversion factor, and units returning functions. 601 return_units = specific_fns.setup.get_specific_fn('return_units', pipes.get_type()) 602 return_grace_string = specific_fns.setup.get_specific_fn('return_grace_string', pipes.get_type()) 603 604 # Test if the axis data type is a minimisation statistic. 605 if data_type[i] and data_type[i] != 'spin' and generic_fns.minimise.return_data_name(data_type[i]): 606 return_units = generic_fns.minimise.return_units 607 return_grace_string = generic_fns.minimise.return_grace_string 608 609 # Some axis default values for spin data. 610 if data_type[i] == 'spin': 611 # Residue only data. 612 if seq_type[i] == 'res': 613 # Axis limits. 614 if not axis_min[i]: 615 axis_min[i] = repr(cdp.mol[0].res[0].num - 1) 616 if not axis_max[i]: 617 axis_max[i] = repr(cdp.mol[0].res[-1].num + 1) 618 619 # X-axis label. 620 if not axis_labels[i]: 621 axis_labels[i] = "Residue number" 622 623 # Spin only data. 624 if seq_type[i] == 'spin': 625 # Axis limits. 626 if not axis_min[i]: 627 axis_min[i] = repr(cdp.mol[0].res[0].spin[0].num - 1) 628 if not axis_max[i]: 629 axis_max[i] = repr(cdp.mol[0].res[0].spin[-1].num + 1) 630 631 # X-axis label. 632 if not axis_labels[i]: 633 axis_labels[i] = "Spin number" 634 635 # Mixed data. 636 if seq_type[i] == 'mixed': 637 # X-axis label. 638 if not axis_labels[i]: 639 axis_labels[i] = "Spin identification string" 640 641 # Some axis default values for other data types. 642 else: 643 # Label. 644 if analysis_spec and not axis_labels[i]: 645 # Get the units. 646 units = return_units(data_type[i]) 647 648 # Set the label. 649 axis_labels[i] = return_grace_string(data_type[i]) 650 651 # Add units. 652 if units: 653 axis_labels[i] = axis_labels[i] + "\\N (" + units + ")" 654 655 # Normalised data. 656 if norm and axes[i] == 'y': 657 axis_labels[i] = axis_labels[i] + " \\N\\q(normalised)\\Q" 658 659 # Write out the data. 660 if axis_min[i] != None: 661 file.write("@ world %smin %s\n" % (axes[i], axis_min[i])) 662 if axis_max[i] != None: 663 file.write("@ world %smax %s\n" % (axes[i], axis_max[i])) 664 if axis_labels[i]: 665 file.write("@ %saxis label \"%s\"\n" % (axes[i], axis_labels[i])) 666 file.write("@ %saxis label char size 1.48\n" % axes[i]) 667 file.write("@ %saxis tick major size 0.75\n" % axes[i]) 668 file.write("@ %saxis tick major linewidth %s\n" % (axes[i], linewidth)) 669 file.write("@ %saxis tick minor linewidth %s\n" % (axes[i], linewidth)) 670 file.write("@ %saxis tick minor size 0.45\n" % axes[i]) 671 file.write("@ %saxis ticklabel char size 1.00\n" % axes[i]) 672 673 # Legend box. 674 if legend_pos: 675 file.write("@ legend %s, %s\n" % (legend_pos[0], legend_pos[1])) 676 if legend: 677 file.write("@ legend off\n") 678 679 # Frame. 680 file.write("@ frame linewidth %s\n" % linewidth) 681 682 # Loop over each graph set. 683 for i in range(sets): 684 # Symbol style (default to all different symbols). 685 if symbols: 686 file.write("@ s%i symbol %i\n" % (i, symbols[i])) 687 else: 688 # The symbol number (cycle between 1 and 10). 689 num = i % 10 + 1 690 691 # Write out. 692 file.write("@ s%i symbol %i\n" % (i, num)) 693 694 # Symbol sizes (default to a small size). 695 if symbol_sizes: 696 file.write("@ s%i symbol size %s\n" % (i, symbol_sizes[i])) 697 else: 698 file.write("@ s%i symbol size 0.45\n" % i) 699 700 # The symbol fill. 701 if symbol_fill: 702 file.write("@ s%i symbol fill pattern %i\n" % (i, symbol_fill[i])) 703 704 # The symbol line width. 705 file.write("@ s%i symbol linewidth %s\n" % (i, linewidth)) 706 707 # Symbol colour (default to nothing). 708 if set_colours: 709 file.write("@ s%i symbol color %s\n" % (i, set_colours[i])) 710 711 # Error bars. 712 file.write("@ s%i errorbar size 0.5\n" % i) 713 file.write("@ s%i errorbar linewidth %s\n" % (i, linewidth)) 714 file.write("@ s%i errorbar riser linewidth %s\n" % (i, linewidth)) 715 716 # Line linestyle (default to nothing). 717 if linestyle: 718 file.write("@ s%i line linestyle %s\n" % (i, linestyle[i])) 719 720 # Line linetype (default to nothing). 721 if linetype: 722 file.write("@ s%i line type %s\n" % (i, linetype[i])) 723 724 # Line colours (default to nothing). 725 if set_colours: 726 file.write("@ s%i line color %s\n" % (i, set_colours[i])) 727 728 # Legend. 729 if set_names and set_names[i]: 730 file.write("@ s%i legend \"%s\"\n" % (i, set_names[i]))
731