source: trunk/python/asaplot.py@ 117

Last change on this file since 117 was 117, checked in by cal103, 21 years ago

Changed from 1-relative to 0-relative as requested by Malte; retabified and
removed code commented-out in the previous revision.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.5 KB
RevLine 
[111]1"""
2ASAP plotting class based on matplotlib.
3"""
4
5import sys
6from re import match
7import Tkinter as Tk
8
9print "Importing matplotlib with TkAgg backend."
10import matplotlib
11matplotlib.use("TkAgg")
12
13from matplotlib.backends import new_figure_manager, show
14from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, \
[117]15 FigureManagerTkAgg
[111]16from matplotlib.figure import Figure, Text
17
18# Force use of the newfangled toolbar.
19matplotlib.rcParams['toolbar'] = 'toolbar2'
20
21# Colour dictionary.
22colours = {}
23
24class ASAPlot:
25 """
26 ASAP plotting class based on matplotlib.
27 """
28
[114]29 def __init__(self, rowcol='11', title='', size=(8,6), buffering=False):
[117]30 """
31 Create a new instance of the ASAPlot plotting class.
32 """
33 self.window = Tk.Tk()
34
35 self.frame1 = Tk.Frame(self.window, relief=Tk.RIDGE, borderwidth=4)
36 self.frame1.pack(fill=Tk.BOTH)
[111]37
[117]38 self.figure = Figure(figsize=size, facecolor='#ddddee')
39 self.canvas = FigureCanvasTkAgg(self.figure, master=self.frame1)
40 self.canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
[111]41
[117]42 # Simply instantiating this is enough to get a working toolbar.
43 self.figmgr = FigureManagerTkAgg(self.canvas, 1, self.window)
44 self.window.wm_title('ASAPlot graphics window')
[111]45
[117]46 self.figure.text(0.5, 0.95, title, horizontalalignment='center')
[111]47
[117]48 self.rows = int(rowcol[0])
49 self.cols = int(rowcol[1])
50 self.subplots = []
51 for i in range(0,self.rows*self.cols):
52 self.subplots.append({})
53 self.subplots[i]['axes'] = self.figure.add_subplot(self.rows,
54 self.cols, i+1)
55 self.subplots[i]['lines'] = []
[111]56
[117]57 self.figmgr.toolbar.set_active([0])
[111]58
[117]59 self.axes = self.subplots[0]['axes']
60 self.lines = self.subplots[0]['lines']
[111]61
62
[117]63 # Set matplotlib default colour sequence.
64 self.colours = [1, 'b', 'g', 'r', 'c', 'm', 'y', 'k', 'w']
65 self.attributes = {}
66 self.loc = 1
[111]67
[117]68 matplotlib.interactive = True
69 self.buffering = buffering
[111]70
[117]71 self.canvas.show()
[111]72
73
[117]74 def clear(self):
75 """
76 Delete all lines from the plot. Line numbering will restart from 1.
77 """
[111]78
[117]79 for i in range(1,len(self.lines)+1):
80 self.delete(i)
[111]81
[117]82 self.axes.clear()
83 self.colours[0] = 1
84 self.lines = []
85
[111]86
87 def delete(self, numbers=None):
[117]88 """
89 Delete the 0-relative line number, default is to delete the last.
90 The remaining lines are NOT renumbered.
91 """
[111]92
[117]93 if numbers is None: numbers = [len(self.lines)-1]
[111]94
[117]95 if not hasattr(numbers, '__iter__'):
96 numbers = [numbers]
[111]97
[117]98 for number in numbers:
99 if 0 <= number < len(self.lines):
100 if self.lines[number] is not None:
101 for line in self.lines[number]:
102 line.set_linestyle('None')
103 self.lines[number] = None
[111]104
[117]105 self.show()
[111]106
107
108 def get_line(self):
[117]109 """
110 Get the current default line attributes.
111 """
112 return self.attributes
[111]113
114
115 def hold(self, hold=True):
[117]116 """
117 Buffer graphics until subsequently released.
118 """
119 self.buffering = hold
[111]120
121
122 def legend(self, loc=1):
[117]123 """
124 Add a legend to the plot.
[111]125
[117]126 Any other value for loc else disables the legend:
127 1: upper right
128 2: upper left
129 3: lower left
130 4: lower right
131 5: right
132 6: center left
133 7: center right
134 8: lower center
135 9: upper center
136 10: center
[111]137
[117]138 """
139 if 1 > loc > 10: loc = 0
140 self.loc = loc
141 self.show()
[111]142
143
144 def map(self):
[117]145 """
146 Reveal the ASAPlot graphics window and bring it to the top of the
147 window stack.
148 """
149 self.window.wm_deiconify()
150 self.window.lift()
[111]151
[117]152
[111]153 def palette(self, pen=None, colours=None):
[117]154 """
155 Redefine the colour sequence.
[111]156
[117]157 pen is the pen number to use for the next plot; this will be auto-
158 incremented.
[111]159
[117]160 colours is the list of pen colours. Colour may be specified via
161 the single letter values understood by matplotlib:
[111]162
[117]163 b: blue
164 g: green
165 r: red
166 c: cyan
167 m: magenta
168 y: yellow
169 k: black
170 w: white
[111]171
[117]172 or via the full name as listed in the colour dictionary which is
173 loaded by default by load_colours() from rgb.txt and listed by
174 list_colours().
175 """
[111]176
[117]177 if pen is None and colours is None:
178 self.colours = []
179 return
[111]180
[117]181 if pen is None:
182 if not len(self.colours):
183 self.colours = [1]
184 else:
185 self.colours[0] = pen
[111]186
[117]187 if colours is None:
188 return
[111]189
[117]190 cols = []
191 for col in colours:
192 cols.append(get_colour(col))
[111]193
[117]194 self.colours[1:] = cols
[111]195
[117]196 if 0 > self.colours[0] > len(self.colours):
197 self.colours[0] = 1
[111]198
199
200 def plot(self, x=None, y=None, mask=None, fmt=None, add=None):
[117]201 """
202 Plot the next line in the current frame using the current line
203 attributes. The ASAPlot graphics window will be mapped and raised.
[111]204
[117]205 The argument list works a bit like the matlab plot() function.
206 """
[111]207
[117]208 if x is None:
209 if y is None: return
210 x = range(len(y))
[111]211
[117]212 elif y is None:
213 y = x
214 x = range(len(y))
[111]215
[117]216 if mask is None:
217 if fmt is None:
218 line = self.axes.plot(x, y)
219 else:
220 line = self.axes.plot(x, y, fmt)
221 else:
222 segments = []
[111]223
[117]224 mask = list(mask)
225 i = 0
226 while mask[i:].count(1):
227 i += mask[i:].index(1)
228 if mask[i:].count(0):
229 j = i + mask[i:].index(0)
230 else:
231 j = len(mask)
[111]232
[117]233 segments.append(x[i:j])
234 segments.append(y[i:j])
[111]235
[117]236 i = j
[111]237
[117]238 line = self.axes.plot(*segments)
[111]239
[117]240 # Add to an existing line?
241 if add is None or len(self.lines) < add < 0:
242 self.lines.append(line)
243 i = len(self.lines) - 1
244 else:
245 if add == 0: add = len(self.lines)
246 i = add - 1
247 self.lines[i].extend(line)
[111]248
[117]249 # Set/reset attributes for the line.
250 gotcolour = False
251 for k, v in self.attributes.iteritems():
252 if k == 'color': gotcolour = True
253 for segment in self.lines[i]:
254 getattr(segment, "set_%s"%k)(v)
[111]255
[117]256 if not gotcolour and len(self.colours):
257 for segment in self.lines[i]:
258 getattr(segment, "set_color")(self.colours[self.colours[0]])
[111]259
[117]260 self.colours[0] += 1
261 if self.colours[0] >= len(self.colours):
262 self.colours[0] = 1
[111]263
[117]264 self.show()
[111]265
266
267 def quit(self):
[117]268 """
269 Destroy the ASAPlot graphics window.
270 """
271 self.window.destroy()
[111]272
273
274 def release(self):
[117]275 """
276 Release buffered graphics.
277 """
278 self.buffering = False
279 self.show()
[111]280
281
282 def set_axes(self, what=None, *args, **kwargs):
[117]283 """
284 Set attributes for the axes by calling the relevant Axes.set_*()
285 method. Colour translation is done as described in the doctext
286 for palette().
287 """
[111]288
[117]289 if what is None: return
290 if what[-6:] == 'colour': what = what[:-6] + 'color'
[111]291
[117]292 newargs = {}
293 for k, v in kwargs.iteritems():
294 k = k.lower()
295 if k == 'colour': k = 'color'
[111]296
[117]297 if k == 'color':
298 v = get_colour(v)
[111]299
[117]300 newargs[k] = v
[111]301
[117]302 getattr(self.axes, "set_%s"%what)(*args, **newargs)
303 self.show()
[111]304
305
306 def set_figure(self, what=None, *args, **kwargs):
[117]307 """
308 Set attributes for the figure by calling the relevant Figure.set_*()
309 method. Colour translation is done as described in the doctext
310 for palette().
311 """
[111]312
[117]313 if what is None: return
314 if what[-6:] == 'colour': what = what[:-6] + 'color'
315 if what[-5:] == 'color' and len(args):
316 args = (get_colour(args[0]),)
[111]317
[117]318 newargs = {}
319 for k, v in kwargs.iteritems():
320 k = k.lower()
321 if k == 'colour': k = 'color'
[111]322
[117]323 if k == 'color':
324 v = get_colour(v)
[111]325
[117]326 newargs[k] = v
[111]327
[117]328 getattr(self.figure, "set_%s"%what)(*args, **newargs)
329 self.show()
[111]330
331
332 def set_line(self, number=None, **kwargs):
[117]333 """
334 Set attributes for the specified line, or else the next line(s)
335 to be plotted.
[111]336
[117]337 number is the 0-relative number of a line that has already been
338 plotted. If no such line exists, attributes are recorded and used
339 for the next line(s) to be plotted.
[111]340
[117]341 Keyword arguments specify Line2D attributes, e.g. color='r'. Do
[111]342
[117]343 import matplotlib
344 help(matplotlib.lines)
[111]345
[117]346 The set_* methods of class Line2D define the attribute names and
347 values. For non-US usage, "colour" is recognized as synonymous with
348 "color".
[111]349
[117]350 Set the value to None to delete an attribute.
[111]351
[117]352 Colour translation is done as described in the doctext for palette().
353 """
[111]354
[117]355 redraw = False
356 for k, v in kwargs.iteritems():
357 k = k.lower()
358 if k == 'colour': k = 'color'
[111]359
[117]360 if k == 'color':
361 v = get_colour(v)
[111]362
[117]363 if 0 <= number < len(self.lines):
364 if self.lines[number] is not None:
365 for line in self.lines[number]:
366 getattr(line, "set_%s"%k)(v)
367 redraw = True
368 else:
369 if v is None:
370 del self.attributes[k]
371 else:
372 self.attributes[k] = v
[111]373
[117]374 if redraw: self.show()
[111]375
376
377 def show(self):
[117]378 """
379 Show graphics dependent on the current buffering state.
380 """
381 if not self.buffering:
382 if self.loc:
383 lines = []
384 labels = []
385 i = 0
386 for line in self.lines:
387 i += 1
388 if line is not None:
389 lines.append(line[0])
390 lbl = line[0].get_label()
391 if lbl == '':
392 lbl = str(i)
393 labels.append(lbl)
[111]394
[117]395 if len(lines):
396 self.axes.legend(tuple(lines), tuple(labels), self.loc)
397 else:
398 self.axes.legend((' '))
[111]399
[117]400 self.window.wm_deiconify()
401 self.canvas.show()
[111]402
403
[117]404 def subplot(self, col=0, row=0):
405 """
406 Set the subplot; 0-relative column and row numbers. Overrange column
407 numbers map onto successive rows.
408 """
409 i = (self.cols*row + col)%(self.rows*self.cols)
410 self.axes = self.subplots[i]['axes']
411 self.lines = self.subplots[i]['lines']
[111]412
[117]413
[111]414 def terminate(self):
[117]415 """
416 Clear the figure.
417 """
418 self.window.destroy()
[111]419
420
421 def text(self, *args, **kwargs):
[117]422 """
423 Add text to the figure.
424 """
425 self.figure.text(*args, **kwargs)
426 self.show()
[111]427
428
429 def unmap(self):
[117]430 """
431 Hide the ASAPlot graphics window.
432 """
433 self.window.wm_withdraw()
[111]434
435
436def get_colour(colour='black'):
437 """
438 Look up a colour by name in the colour dictionary. Matches are
439 case-insensitive, insensitive to blanks, and 'gray' matches 'grey'.
440 """
441
442 if colour is None: return None
443
444 if match('[rgbcmykw]$', colour): return colour
445 if match('#[\da-fA-F]{6}$', colour): return colour
446
447 if len(colours) == 0: load_colours()
448
449 # Try a quick match.
450 if colours.has_key(colour): return colours[colour]
451
452 colour = colour.replace(' ','').lower()
453 colour = colour.replace('gray','grey')
454 for name in colours.keys():
[117]455 if name.lower() == colour:
456 return colours[name]
[111]457
458 return '#000000'
459
460
461def list_colours():
462 """
463 List the contents of the colour dictionary sorted by name.
464 """
465
466 if len(colours) == 0: load_colours()
467
468 names = colours.keys()
469 names.sort()
470 for name in names:
[117]471 print colours[name], name
[111]472
473
474def load_colours(file='/usr/local/lib/rgb.txt'):
475 """
476 Load the colour dictionary from the specified file.
477 """
478 print 'Loading colour dictionary from', file
479 rgb = open(file, 'r')
480
481 while True:
[117]482 line = rgb.readline()
483 if line == '': break
484 tmp = line.split()
[111]485
[117]486 if len(tmp) == 4:
487 if tmp[3][:4] == 'gray': continue
488 if tmp[3].lower().find('gray') != -1: continue
[111]489
[117]490 name = tmp[3][0].upper() + tmp[3][1:]
491 r, g, b = int(tmp[0]), int(tmp[1]), int(tmp[2])
492 colours[name] = '#%2.2x%2.2x%2.2x' % (r, g, b)
Note: See TracBrowser for help on using the repository browser.