source: trunk/python/asaplot.py @ 114

Last change on this file since 114 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.