Changeset 111 for trunk


Ignore:
Timestamp:
12/01/04 11:00:00 (20 years ago)
Author:
cal103
Message:

Initial revision

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/python/asaplot.py

    r109 r111  
     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='', 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=(10,8), 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
     108        self.lines = []
     109
     110
     111    def delete(self, numbers=None):
     112        """
     113        Delete the 1-relative line number, default is to delete the last.
     114        The remaining lines are NOT renumbered.
     115        """
     116
     117        if numbers is None: numbers = [len(self.lines)]
     118
     119        if not hasattr(numbers, '__iter__'):
     120            numbers = [numbers]
     121
     122        for number in numbers:
     123            if 0 < number <= len(self.lines):
     124                if self.lines[number-1] is not None:
     125                    for line in self.lines[number-1]:
     126                        line.set_linestyle('None')
     127                        self.lines[number-1] = None
     128
     129        self.show()
     130
     131
     132    def get_line(self):
     133        """
     134        Get the current default line attributes.
     135        """
     136        return self.attributes
     137
     138
     139    def hold(self, hold=True):
     140        """
     141        Buffer graphics until subsequently released.
     142        """
     143        self.buffering = hold
     144        if self.buffering:
     145            self.label1.config(text='On hold')
     146        else:
     147            self.label1.config(text='')
     148
     149
     150    def legend(self, loc=1):
     151        """
     152        Add a legend to the plot.
     153
     154        Any other value for loc else disables the legend:
     155             1: upper right
     156             2: upper left
     157             3: lower left
     158             4: lower right
     159             5: right
     160             6: center left
     161             7: center right
     162             8: lower center
     163             9: upper center
     164            10: center
     165
     166        """
     167        if 1 > loc > 10: loc = 0
     168        self.loc = loc
     169        self.show()
     170
     171
     172    def map(self):
     173        """
     174        Reveal the ASAPlot graphics window and bring it to the top of the
     175        window stack.
     176        """
     177        self.window.wm_deiconify()
     178        self.window.lift()
     179
     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                        labels.append(str(i))
     419
     420                if len(lines):
     421                    self.axes.legend(tuple(lines), tuple(labels), self.loc)
     422                else:
     423                    self.axes.legend((' '))
     424
     425            self.window.wm_deiconify()
     426            self.canvas.show()
     427
     428
     429    def subplot(self, col=1, row=1):
     430        """
     431        Set the subplot.
     432        """
     433        i = (self.cols*(row-1) + col-1)%(self.rows*self.cols)
     434        self.axes  = self.subplots[i]['axes']
     435        self.lines = self.subplots[i]['lines']
     436
     437
     438    def terminate(self):
     439        """
     440        Clear the figure.
     441        """
     442        self.window.destroy()
     443
     444
     445    def text(self, *args, **kwargs):
     446        """
     447        Add text to the figure.
     448        """
     449        self.figure.text(*args, **kwargs)
     450        self.show()
     451
     452
     453    def unmap(self):
     454        """
     455        Hide the ASAPlot graphics window.
     456        """
     457        self.window.wm_withdraw()
     458
     459
     460def get_colour(colour='black'):
     461    """
     462    Look up a colour by name in the colour dictionary.  Matches are
     463    case-insensitive, insensitive to blanks, and 'gray' matches 'grey'.
     464    """
     465
     466    if colour is None: return None
     467
     468    if match('[rgbcmykw]$', colour): return colour
     469    if match('#[\da-fA-F]{6}$', colour): return colour
     470
     471    if len(colours) == 0: load_colours()
     472
     473    # Try a quick match.
     474    if colours.has_key(colour): return colours[colour]
     475
     476    colour = colour.replace(' ','').lower()
     477    colour = colour.replace('gray','grey')
     478    for name in colours.keys():
     479        if name.lower() == colour:
     480            return colours[name]
     481
     482    return '#000000'
     483
     484
     485def list_colours():
     486    """
     487    List the contents of the colour dictionary sorted by name.
     488    """
     489
     490    if len(colours) == 0: load_colours()
     491
     492    names = colours.keys()
     493    names.sort()
     494    for name in names:
     495        print colours[name], name
     496
     497
     498def load_colours(file='/usr/local/lib/rgb.txt'):
     499    """
     500    Load the colour dictionary from the specified file.
     501    """
     502    print 'Loading colour dictionary from', file
     503    rgb = open(file, 'r')
     504
     505    while True:
     506        line = rgb.readline()
     507        if line == '': break
     508        tmp = line.split()
     509
     510        if len(tmp) == 4:
     511            if tmp[3][:4] == 'gray': continue
     512            if tmp[3].lower().find('gray') != -1: continue
     513
     514            name = tmp[3][0].upper() + tmp[3][1:]
     515            r, g, b = int(tmp[0]), int(tmp[1]), int(tmp[2])
     516            colours[name] = '#%2.2x%2.2x%2.2x' % (r, g, b)
Note: See TracChangeset for help on using the changeset viewer.