source: trunk/python/asaplot.py @ 117

Last change on this file since 117 was 117, checked in by cal103, 20 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.