source: trunk/python/asaplot.py@ 112

Last change on this file since 112 was 111, checked in by cal103, 20 years ago

Initial revision

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