source: trunk/python/asaplot.py @ 114

Last change on this file since 114 was 114, checked in by mar637, 19 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
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='', 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)
37
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)
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        self.axes.clear()
108        self.colours[0] = 1
109        self.lines = []
110       
111
112    def delete(self, numbers=None):
113        """
114        Delete the 1-relative line number, default is to delete the last.
115        The remaining lines are NOT renumbered.
116        """
117
118        if numbers is None: numbers = [len(self.lines)]
119
120        if not hasattr(numbers, '__iter__'):
121            numbers = [numbers]
122
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
129
130        self.show()
131
132
133    def get_line(self):
134        """
135        Get the current default line attributes.
136        """
137        return self.attributes
138
139
140    def hold(self, hold=True):
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='')
149
150
151    def legend(self, loc=1):
152        """
153        Add a legend to the plot.
154
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
166
167        """
168        if 1 > loc > 10: loc = 0
169        self.loc = loc
170        self.show()
171
172
173    def map(self):
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()
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                        lbl = line[0].get_label()
419                        if lbl == '':
420                            lbl = str(i)
421                        labels.append(lbl)
422
423                if len(lines):
424                    self.axes.legend(tuple(lines), tuple(labels), self.loc)
425                else:
426                    self.axes.legend((' '))
427
428            self.window.wm_deiconify()
429            self.canvas.show()
430
431    def subplot(self, col=1, row=1):
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']
438
439
440    def terminate(self):
441        """
442        Clear the figure.
443        """
444        self.window.destroy()
445
446
447    def text(self, *args, **kwargs):
448        """
449        Add text to the figure.
450        """
451        self.figure.text(*args, **kwargs)
452        self.show()
453
454
455    def unmap(self):
456        """
457        Hide the ASAPlot graphics window.
458        """
459        self.window.wm_withdraw()
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():
481        if name.lower() == colour:
482            return colours[name]
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:
497        print colours[name], name
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:
508        line = rgb.readline()
509        if line == '': break
510        tmp = line.split()
511
512        if len(tmp) == 4:
513            if tmp[3][:4] == 'gray': continue
514            if tmp[3].lower().find('gray') != -1: continue
515
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.