source: trunk/python/asaplot.py@ 116

Last change on this file since 116 was 114, checked in by mar637, 20 years ago

Miscellaneous fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.1 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, \
[114]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):
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
[114]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
[114]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
[114]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)
[111]52
[114]53## self.button = Tk.Button(master=self.frame2, text='Dismiss',
54## command=self.quit)
55## self.button.pack(side=Tk.RIGHT)
[111]56
[114]57 self.figure.text(0.5, 0.95, title, horizontalalignment='center')
[111]58
[114]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'] = []
[111]67
[114]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
[111]73
[114]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='')
[111]80
[114]81## self.canvas.get_tk_widget().bind("<Motion>", mouse_move)
82 self.figmgr.toolbar.set_active([0])
[111]83
[114]84 self.axes = self.subplots[0]['axes']
85 self.lines = self.subplots[0]['lines']
[111]86
87
[114]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
[111]92
[114]93 matplotlib.interactive = True
94 self.buffering = buffering
[111]95
[114]96 #self.figmgr.show()
97 self.canvas.show()
[111]98
99
100 def clear(self):
[114]101 """
102 Delete all lines from the plot. Line numbering will restart from 1.
103 """
[111]104
[114]105 for i in range(1,len(self.lines)+1):
106 self.delete(i)
107 self.axes.clear()
108 self.colours[0] = 1
109 self.lines = []
110
[111]111
112 def delete(self, numbers=None):
[114]113 """
114 Delete the 1-relative line number, default is to delete the last.
115 The remaining lines are NOT renumbered.
116 """
[111]117
[114]118 if numbers is None: numbers = [len(self.lines)]
[111]119
[114]120 if not hasattr(numbers, '__iter__'):
121 numbers = [numbers]
[111]122
[114]123 for number in numbers:
124 if 0 < number <= len(self.lines):
125 if self.lines[number-1] is not None:
126 for line in self.lines[number-1]:
127 line.set_linestyle('None')
128 self.lines[number-1] = None
[111]129
[114]130 self.show()
[111]131
132
133 def get_line(self):
[114]134 """
135 Get the current default line attributes.
136 """
137 return self.attributes
[111]138
139
140 def hold(self, hold=True):
[114]141 """
142 Buffer graphics until subsequently released.
143 """
144 self.buffering = hold
145## if self.buffering:
146## self.label1.config(text='On hold')
147## else:
148## self.label1.config(text='')
[111]149
150
151 def legend(self, loc=1):
[114]152 """
153 Add a legend to the plot.
[111]154
[114]155 Any other value for loc else disables the legend:
156 1: upper right
157 2: upper left
158 3: lower left
159 4: lower right
160 5: right
161 6: center left
162 7: center right
163 8: lower center
164 9: upper center
165 10: center
[111]166
[114]167 """
168 if 1 > loc > 10: loc = 0
169 self.loc = loc
170 self.show()
[111]171
172
173 def map(self):
[114]174 """
175 Reveal the ASAPlot graphics window and bring it to the top of the
176 window stack.
177 """
178 self.window.wm_deiconify()
179 self.window.lift()
[111]180
181 def palette(self, pen=None, colours=None):
[114]182 """
183 Redefine the colour sequence.
[111]184
[114]185 pen is the pen number to use for the next plot; this will be auto-
186 incremented.
[111]187
[114]188 colours is the list of pen colours. Colour may be specified via
189 the single letter values understood by matplotlib:
[111]190
[114]191 b: blue
192 g: green
193 r: red
194 c: cyan
195 m: magenta
196 y: yellow
197 k: black
198 w: white
[111]199
[114]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 """
[111]204
[114]205 if pen is None and colours is None:
206 self.colours = []
207 return
[111]208
[114]209 if pen is None:
210 if not len(self.colours):
211 self.colours = [1]
212 else:
213 self.colours[0] = pen
[111]214
[114]215 if colours is None:
216 return
[111]217
[114]218 cols = []
219 for col in colours:
220 cols.append(get_colour(col))
[111]221
[114]222 self.colours[1:] = cols
[111]223
[114]224 if 0 > self.colours[0] > len(self.colours):
225 self.colours[0] = 1
[111]226
227
228 def plot(self, x=None, y=None, mask=None, fmt=None, add=None):
[114]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.
[111]232
[114]233 The argument list works a bit like the matlab plot() function.
234 """
[111]235
[114]236 if x is None:
237 if y is None: return
238 x = range(len(y))
[111]239
[114]240 elif y is None:
241 y = x
242 x = range(len(y))
[111]243
[114]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 = []
[111]251
[114]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)
[111]260
[114]261 segments.append(x[i:j])
262 segments.append(y[i:j])
[111]263
[114]264 i = j
[111]265
[114]266 line = self.axes.plot(*segments)
[111]267
[114]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)
[111]276
[114]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)
[111]283
[114]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]])
[111]287
[114]288 self.colours[0] += 1
289 if self.colours[0] >= len(self.colours):
290 self.colours[0] = 1
[111]291
[114]292 self.show()
[111]293
294
295 def quit(self):
[114]296 """
297 Destroy the ASAPlot graphics window.
298 """
299 self.window.destroy()
[111]300
301
302 def release(self):
[114]303 """
304 Release buffered graphics.
305 """
306 self.buffering = False
307 self.show()
[111]308
309
310 def set_axes(self, what=None, *args, **kwargs):
[114]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 """
[111]316
[114]317 if what is None: return
318 if what[-6:] == 'colour': what = what[:-6] + 'color'
[111]319
[114]320 newargs = {}
321 for k, v in kwargs.iteritems():
322 k = k.lower()
323 if k == 'colour': k = 'color'
[111]324
[114]325 if k == 'color':
326 v = get_colour(v)
[111]327
[114]328 newargs[k] = v
[111]329
[114]330 getattr(self.axes, "set_%s"%what)(*args, **newargs)
331 self.show()
[111]332
333
334 def set_figure(self, what=None, *args, **kwargs):
[114]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 """
[111]340
[114]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]),)
[111]345
[114]346 newargs = {}
347 for k, v in kwargs.iteritems():
348 k = k.lower()
349 if k == 'colour': k = 'color'
[111]350
[114]351 if k == 'color':
352 v = get_colour(v)
[111]353
[114]354 newargs[k] = v
[111]355
[114]356 getattr(self.figure, "set_%s"%what)(*args, **newargs)
357 self.show()
[111]358
359
360 def set_line(self, number=None, **kwargs):
[114]361 """
362 Set attributes for the specified line, or else the next line(s)
363 to be plotted.
[111]364
[114]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.
[111]368
[114]369 Keyword arguments specify Line2D attributes, e.g. color='r'. Do
[111]370
[114]371 import matplotlib
372 help(matplotlib.lines)
[111]373
[114]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".
[111]377
[114]378 Set the value to None to delete an attribute.
[111]379
[114]380 Colour translation is done as described in the doctext for palette().
381 """
[111]382
[114]383 redraw = False
384 for k, v in kwargs.iteritems():
385 k = k.lower()
386 if k == 'colour': k = 'color'
[111]387
[114]388 if k == 'color':
389 v = get_colour(v)
[111]390
[114]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
[111]401
[114]402 if redraw: self.show()
[111]403
404
405 def show(self):
[114]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 lbl = line[0].get_label()
419 if lbl == '':
420 lbl = str(i)
421 labels.append(lbl)
[111]422
[114]423 if len(lines):
424 self.axes.legend(tuple(lines), tuple(labels), self.loc)
425 else:
426 self.axes.legend((' '))
[111]427
[114]428 self.window.wm_deiconify()
429 self.canvas.show()
[111]430
431 def subplot(self, col=1, row=1):
[114]432 """
433 Set the subplot.
434 """
435 i = (self.cols*(row-1) + col-1)%(self.rows*self.cols)
436 self.axes = self.subplots[i]['axes']
437 self.lines = self.subplots[i]['lines']
[111]438
439
440 def terminate(self):
[114]441 """
442 Clear the figure.
443 """
444 self.window.destroy()
[111]445
446
447 def text(self, *args, **kwargs):
[114]448 """
449 Add text to the figure.
450 """
451 self.figure.text(*args, **kwargs)
452 self.show()
[111]453
454
455 def unmap(self):
[114]456 """
457 Hide the ASAPlot graphics window.
458 """
459 self.window.wm_withdraw()
[111]460
461
462def get_colour(colour='black'):
463 """
464 Look up a colour by name in the colour dictionary. Matches are
465 case-insensitive, insensitive to blanks, and 'gray' matches 'grey'.
466 """
467
468 if colour is None: return None
469
470 if match('[rgbcmykw]$', colour): return colour
471 if match('#[\da-fA-F]{6}$', colour): return colour
472
473 if len(colours) == 0: load_colours()
474
475 # Try a quick match.
476 if colours.has_key(colour): return colours[colour]
477
478 colour = colour.replace(' ','').lower()
479 colour = colour.replace('gray','grey')
480 for name in colours.keys():
[114]481 if name.lower() == colour:
482 return colours[name]
[111]483
484 return '#000000'
485
486
487def list_colours():
488 """
489 List the contents of the colour dictionary sorted by name.
490 """
491
492 if len(colours) == 0: load_colours()
493
494 names = colours.keys()
495 names.sort()
496 for name in names:
[114]497 print colours[name], name
[111]498
499
500def load_colours(file='/usr/local/lib/rgb.txt'):
501 """
502 Load the colour dictionary from the specified file.
503 """
504 print 'Loading colour dictionary from', file
505 rgb = open(file, 'r')
506
507 while True:
[114]508 line = rgb.readline()
509 if line == '': break
510 tmp = line.split()
[111]511
[114]512 if len(tmp) == 4:
513 if tmp[3][:4] == 'gray': continue
514 if tmp[3].lower().find('gray') != -1: continue
[111]515
[114]516 name = tmp[3][0].upper() + tmp[3][1:]
517 r, g, b = int(tmp[0]), int(tmp[1]), int(tmp[2])
518 colours[name] = '#%2.2x%2.2x%2.2x' % (r, g, b)
Note: See TracBrowser for help on using the repository browser.