source: trunk/python/asaplot.py @ 117

Last change on this file since 117 was 117, checked in by cal103, 19 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
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.figure.text(0.5, 0.95, title, horizontalalignment='center')
47
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'] = []
56
57        self.figmgr.toolbar.set_active([0])
58
59        self.axes  = self.subplots[0]['axes']
60        self.lines = self.subplots[0]['lines']
61
62
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
67
68        matplotlib.interactive = True
69        self.buffering = buffering
70
71        self.canvas.show()
72
73
74    def clear(self):
75        """
76        Delete all lines from the plot.  Line numbering will restart from 1.
77        """
78
79        for i in range(1,len(self.lines)+1):
80           self.delete(i)
81
82        self.axes.clear()
83        self.colours[0] = 1
84        self.lines = []
85       
86
87    def delete(self, numbers=None):
88        """
89        Delete the 0-relative line number, default is to delete the last.
90        The remaining lines are NOT renumbered.
91        """
92
93        if numbers is None: numbers = [len(self.lines)-1]
94
95        if not hasattr(numbers, '__iter__'):
96            numbers = [numbers]
97
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
104
105        self.show()
106
107
108    def get_line(self):
109        """
110        Get the current default line attributes.
111        """
112        return self.attributes
113
114
115    def hold(self, hold=True):
116        """
117        Buffer graphics until subsequently released.
118        """
119        self.buffering = hold
120
121
122    def legend(self, loc=1):
123        """
124        Add a legend to the plot.
125
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
137
138        """
139        if 1 > loc > 10: loc = 0
140        self.loc = loc
141        self.show()
142
143
144    def map(self):
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()
151
152
153    def palette(self, pen=None, colours=None):
154        """
155        Redefine the colour sequence.
156
157        pen is the pen number to use for the next plot; this will be auto-
158        incremented.
159
160        colours is the list of pen colours.  Colour may be specified via
161        the single letter values understood by matplotlib:
162
163            b: blue
164            g: green
165            r: red
166            c: cyan
167            m: magenta
168            y: yellow
169            k: black
170            w: white
171
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        """
176
177        if pen is None and colours is None:
178            self.colours = []
179            return
180
181        if pen is None:
182            if not len(self.colours):
183                self.colours = [1]
184        else:
185            self.colours[0] = pen
186
187        if colours is None:
188            return
189
190        cols = []
191        for col in colours:
192            cols.append(get_colour(col))
193
194        self.colours[1:] = cols
195
196        if 0 > self.colours[0] > len(self.colours):
197            self.colours[0] = 1
198
199
200    def plot(self, x=None, y=None, mask=None, fmt=None, add=None):
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.
204
205        The argument list works a bit like the matlab plot() function.
206        """
207
208        if x is None:
209            if y is None: return
210            x = range(len(y))
211
212        elif y is None:
213            y = x
214            x = range(len(y))
215
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 = []
223
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)
232
233                segments.append(x[i:j])
234                segments.append(y[i:j])
235
236                i = j
237
238            line = self.axes.plot(*segments)
239
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)
248
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)
255
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]])
259
260            self.colours[0] += 1
261            if self.colours[0] >= len(self.colours):
262                self.colours[0] = 1
263
264        self.show()
265
266
267    def quit(self):
268        """
269        Destroy the ASAPlot graphics window.
270        """
271        self.window.destroy()
272
273
274    def release(self):
275        """
276        Release buffered graphics.
277        """
278        self.buffering = False
279        self.show()
280
281
282    def set_axes(self, what=None, *args, **kwargs):
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        """
288
289        if what is None: return
290        if what[-6:] == 'colour': what = what[:-6] + 'color'
291
292        newargs = {}
293        for k, v in kwargs.iteritems():
294            k = k.lower()
295            if k == 'colour': k = 'color'
296
297            if k == 'color':
298                v = get_colour(v)
299
300            newargs[k] = v
301
302        getattr(self.axes, "set_%s"%what)(*args, **newargs)
303        self.show()
304
305
306    def set_figure(self, what=None, *args, **kwargs):
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        """
312
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]),)
317
318        newargs = {}
319        for k, v in kwargs.iteritems():
320            k = k.lower()
321            if k == 'colour': k = 'color'
322
323            if k == 'color':
324                v = get_colour(v)
325
326            newargs[k] = v
327
328        getattr(self.figure, "set_%s"%what)(*args, **newargs)
329        self.show()
330
331
332    def set_line(self, number=None, **kwargs):
333        """
334        Set attributes for the specified line, or else the next line(s)
335        to be plotted.
336
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.
340
341        Keyword arguments specify Line2D attributes, e.g. color='r'.  Do
342
343            import matplotlib
344            help(matplotlib.lines)
345
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".
349
350        Set the value to None to delete an attribute.
351
352        Colour translation is done as described in the doctext for palette().
353        """
354
355        redraw = False
356        for k, v in kwargs.iteritems():
357            k = k.lower()
358            if k == 'colour': k = 'color'
359
360            if k == 'color':
361                v = get_colour(v)
362
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
373
374        if redraw: self.show()
375
376
377    def show(self):
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)
394
395                if len(lines):
396                    self.axes.legend(tuple(lines), tuple(labels), self.loc)
397                else:
398                    self.axes.legend((' '))
399
400            self.window.wm_deiconify()
401            self.canvas.show()
402
403
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']
412
413
414    def terminate(self):
415        """
416        Clear the figure.
417        """
418        self.window.destroy()
419
420
421    def text(self, *args, **kwargs):
422        """
423        Add text to the figure.
424        """
425        self.figure.text(*args, **kwargs)
426        self.show()
427
428
429    def unmap(self):
430        """
431        Hide the ASAPlot graphics window.
432        """
433        self.window.wm_withdraw()
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():
455        if name.lower() == colour:
456            return colours[name]
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:
471        print colours[name], name
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:
482        line = rgb.readline()
483        if line == '': break
484        tmp = line.split()
485
486        if len(tmp) == 4:
487            if tmp[3][:4] == 'gray': continue
488            if tmp[3].lower().find('gray') != -1: continue
489
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.