Logo Search packages:      
Sourcecode: mascyma version File versions  Download package

backend_gtk.py

from __future__ import division

import os, sys, math

try:  # todo: test this with default RHL8 install.
    import pygtk
    pygtk.require('2.0')
except:
    print sys.exc_info()[1]
    print >>sys.stderr, 'matplotlib requires pygtk-1.99.16 or greater -- trying anyway.  Please hold on'


import gobject
import gtk
from gtk import gdk
import pango

from Numeric import array, Int16

from matplotlib.cbook import iterable, is_string_like, flatten, enumerate, True, False
from matplotlib.transforms import Transform, Bound1D, Bound2D
from matplotlib.backend_bases import RendererBase, GraphicsContextBase,\
     AxisTextBase, FigureBase, FigureManagerBase, _process_text_args
from matplotlib._matlab_helpers import Gcf
from matplotlib.artist import Artist

# the true dots per inch on the screen; should be display dependent
# see http://groups.google.com/groups?q=screen+dpi+x11&hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=off&selm=7077.26e81ad5%40swift.cs.tcd.ie&rnum=5 for some info about screen dpi
SCREENDPI = 75

class ColorManagerGTK:
    _cached = {}  # a map from get_color args to colors
    _cmap = None
    
    def set_drawing_area(self, da):
        self._cmap = da.get_colormap()

    def get_color(self, rgb):
        """
        RGB is a unit RGB tuple, return a gtk.gdk.Color
        """

        try: return self._cached[tuple(rgb)]
        except KeyError: pass
        
        if self._cmap is None:
            raise RuntimeError('First set the drawing area!')

        #print 'rgb is', rgb
        r,g,b = rgb
        color = self._cmap.alloc_color(int(r*65025),int(g*65025),int(b*65025))
        self._cached[tuple(rgb)] = color
        return color

colorManager = ColorManagerGTK()

class RendererGTK(RendererBase):
    def __init__(self, gdkDrawable, dpi):
        self.gdkDrawable = gdkDrawable
        self.width, self.height = self.gdkDrawable.get_size()
        self.dpi = dpi
        
    def draw_arc(self, gc, faceColor, x, y, width, height, angle1, angle2):
        """
        Draw an arc centered at x,y with width and height
        """
        x, y = int(x-0.5*width), self.height-int(y+0.5*height)
        w, h = int(width)+1, int(height)+1
        a1, a2 = int(angle1*64), int(angle2*64)

        if faceColor is not None:            
            rgb = gc.get_rgb()
            gc.set_foreground(faceColor)
            self.gdkDrawable.draw_arc(gc.gdkGC, True, x, y, w, h, a1, a2)
            gc.set_foreground(rgb)
        self.gdkDrawable.draw_arc(gc.gdkGC, False, x, y, w, h, a1, a2)
            
    def draw_line(self, gc, x1, y1, x2, y2):
        """
        Draw a single line from x1,y1 to x2,y2
        """
        self.gdkDrawable.draw_line(
            gc.gdkGC, int(x1), self.height-int(y1),
            int(x2), self.height-int(y2))


    def draw_lines(self, gc, x, y):
        # todo: change signature to draw_lines(self, gc, x, y)
        # todo: this is darn ineffiencient!
        y = self.height - y.astype(Int16)
        x = x.astype(Int16)
        self.gdkDrawable.draw_lines(gc.gdkGC, zip(x,y))

    def draw_point(self, gc, x, y):
        """
        Draw a single point at x,y
        """
        self.gdkDrawable.draw_point(
            gc.gdkGC, int(x), self.height-int(y))

    def draw_polygon(self, gc, faceColor, points):
        """
        Draw a polygon.  points is a len vertices tuple, each element
        giving the x,y coords a vertex

        If faceColor is not None, fill the rectangle with it.  gcEdge
        is a GraphicsContext instance

        """
        points = [(int(x), self.height-int(y)) for x,y in points]
        if faceColor is not None:
            rgb = gc.get_rgb()  #cache color
            gc.set_foreground(faceColor)
            self.gdkDrawable.draw_polygon(gc.gdkGC, False, points)
            gc.set_foreground(rgb)  # restore color
        self.gdkDrawable.draw_polygon(gc.gdkGC, True, points)


    def draw_rectangle(self, gc, faceColor, x, y, width, height):
        """
        Draw a rectangle at lower left x,y with width and height
        If filled=True, fill the rectangle with the gc foreground
        gc is a GraphicsContext instance
        """
        
        #x, y = int(x), self.height-int(y+height)
        #w, h = int(width), int(height)
        x, y = int(x), self.height-int(math.ceil(y+height))
        w, h = int(math.ceil(width)), int(math.ceil(height))


        needEdge=True
        if faceColor is not None:            
            rgb = gc.get_rgb()  #cache color
            gc.set_foreground(faceColor)
            self.gdkDrawable.draw_rectangle(gc.gdkGC, True, x, y, w, h)
            if gc.get_rgb()==rgb: needEdge = False 
            gc.set_foreground(rgb)  # restore color
        if needEdge:
            self.gdkDrawable.draw_rectangle(gc.gdkGC, False, x, y, w, h)
    def new_gc(self):
        return GraphicsContextGTK(self.gdkDrawable.new_gc(), self)


class GraphicsContextGTK(GraphicsContextBase):

    _joind = {
        'bevel' : gdk.JOIN_BEVEL,
        'miter' : gdk.JOIN_MITER,
        'round' : gdk.JOIN_ROUND,
        }

    _capd = {
        'butt' : gdk.CAP_BUTT,
        'projecting' : gdk.CAP_PROJECTING,
        'round' : gdk.CAP_ROUND,
        }

              
    def __init__(self, gdkGC, drawable):
            GraphicsContextBase.__init__(self)
            self.gdkGC = gdkGC
            self.drawable = drawable

    def set_clip_rectangle(self, rectangle):
        GraphicsContextBase.set_clip_rectangle(self, rectangle)
        l,b,w,h = rectangle
        rectangle = (int(l), self.drawable.height-int(b+h),
                     int(w), int(h))
        self.gdkGC.set_clip_rectangle(rectangle)        


    def set_dashes(self, dash_offset, dash_list):
        GraphicsContextBase.set_dashes(self, dash_offset, dash_list)
        dpi = self.drawable.dpi.get()
        if dash_list is not None:
            dashes = dash_list*dpi/72.0
            self.gdkGC.line_style = gdk.LINE_ON_OFF_DASH
            dl = [int(math.ceil(val)) for val in dash_list]
            self.gdkGC.set_dashes(dash_offset, dl)
        else:
            self.gdkGC.line_style = gdk.LINE_SOLID
    def set_foreground(self, fg):
        """
        Set the foreground color.  fg can be a matlab format string, a
        html hex color string, an rgb unit tuple, or a float between 0
        and 1.  In the latter case, grayscale is used.
        """
        GraphicsContextBase.set_foreground(self, fg)
        self.gdkGC.foreground = self._get_gdk_color()

    def set_graylevel(self, frac):
        """
        Set the foreground color to be a gray level with frac frac
        """
        GraphicsContextBase.set_graylevel(self, frac)
        self.gdkGC.foreground = self._get_gdk_color()
        

    def set_linewidth(self, w):
        if w>0 and w<1: w = 1
        GraphicsContextBase.set_linewidth(self, w)
        self.gdkGC.line_width = self._linewidth

    def set_linestyle(self, style):
        GraphicsContextBase.set_linestyle(self, style)
        offset, dashes = self._dashd[style]
        self.set_dashes(offset, dashes)
                                               
    def set_capstyle(self, cs):
        """
        Set the capstyle as a string in ('butt', 'round', 'projecting')
        """
        GraphicsContextBase.set_capstyle(self, cs)
        self.gdkGC.cap_style = self._capd[self._capstyle]

    def set_joinstyle(self, js):
        """
        Set the join style to be one of ('miter', 'round', 'bevel')
        """
        GraphicsContextBase.set_joinstyle(self, js)
        self.gdkGC.join_style = self._joind[self._joinstyle]

    def _get_gdk_color(self):
        return colorManager.get_color(self.get_rgb())


    def _update_gc_line_attributes(self):
        lw = self._linewidth
        js = self._joind[self._joinstyle]
        cs = self._capd[self._capstyle]
        
class FigureGTK(FigureBase, gtk.DrawingArea):
    def __init__(self, figsize, dpi):
        dpi = 72.0/SCREENDPI*dpi
        FigureBase.__init__(self, figsize, dpi)
        gtk.DrawingArea.__init__(self)
        self.set_double_buffered(False)
        w = self.bbox.x.interval()
        h = self.bbox.y.interval()
        self.set_size_request(int(w), int(h))

        #self.connect('focus_in_event', self.focus_in_event)
        self.connect('key_press_event', self.key_press_event)
        self.connect('key_release_event', self.key_release_event)
        self.connect('expose_event', self.expose_event)
        self.connect('configure_event', self.configure_event)
        self.connect('realize', self.realize)
        self.connect('motion_notify_event', self.motion_notify_event)
        self.connect('button_press_event', self.button_press_event)
        self.connect('button_release_event', self.button_release_event)

        self.set_events(
            #gdk.FOCUS_CHANGE_MASK|
            gdk.KEY_PRESS_MASK|
            gdk.KEY_RELEASE_MASK|
            gdk.EXPOSURE_MASK |
            gdk.LEAVE_NOTIFY_MASK |
            gdk.BUTTON_PRESS_MASK |
            gdk.BUTTON_RELEASE_MASK |
            gdk.POINTER_MOTION_MASK )
        

        self._inExpose = False
        self._isConfigured = False
        self._isRealized = False
        self._printQued = []

        self.grey = ( 0.7, 0.7, 0.7)
        self._drawingArea = None
        self._drawable = None
        self._doplot = True    # if False don't plot, just print

    def set_do_plot(self, b):
        self._doplot = b

    def add_axis(self, a):
        FigureBase.add_axis(self, a)
        a.set_child_attr('_drawingArea', self._drawingArea)
        a.set_renderer(self.drawable)
        
    def button_press_event(self, widget, event):
        pass
    def button_release_event(self, widget, event):
        pass
    def motion_notify_event(self, widget, event):
        pass
    def key_press_event(self, widget, event):
        pass
    def key_release_event(self, widget, event):
        pass
        
        
    def configure_event(self, widget, event=None):

        w,h = widget.window.get_size()
        if w==1 or h==1: return # empty fig
        # handle window resizes properly
        if not self._isConfigured:
            self._origWidth, self._origHeight = w, h

        self.bbox.set_bounds(0, 0, w, h)
        scalew = w/self._origWidth
        scaleh = h/self._origHeight

        for a in self.axes:
            a.wash_brushes()
            a.resize()

        for t in self._text:
            t.wash_brushes()

        self._update_renderer(widget)
            
        if self._isConfigured and self._isRealized and self._doplot:
            self.draw()

        self._isConfigured = True
        return gtk.TRUE
        

    def draw(self, drawable=None, *args, **kwargs):
        if drawable is None: drawable=self.drawable
        if drawable is None: return 
        self.drawable = drawable
        if not self._isRealized: return 

        gc = drawable.new_gc()

        width, height = self.bbox.x.interval(), self.bbox.y.interval()
        pixmap = RendererGTK(gtk.gdk.Pixmap(
            self.drawable.gdkDrawable,
            width, height), self.dpi)
                             
        if self._inExpose:
            # override the clip
            gc.set_clip_rectangle(self.bbox.get_bounds())
            
        gc.set_foreground(self.grey)
        pixmap.gdkDrawable.draw_rectangle(
            gc.gdkGC, 1, 0, 0, width, height)

        for a in self.axes:
            a.draw(pixmap)
        for t in self._text:
            t.draw(pixmap)
            
        drawable.gdkDrawable.draw_drawable(
            gc.gdkGC, pixmap.gdkDrawable, 0, 0, 0, 0, width, height)


    def expose_event(self, widget, event):
        if self._isConfigured and self._isRealized:
            self._inExpose = True
            for a in self.axes:
                a.wash_brushes()
            if self._doplot: self.draw()
            self._inExpose = False
        return gtk.TRUE

    def focus_in_event(self, widget, event):
        return gtk.TRUE


    def print_figure(self, filename, dpi=150):
        root, ext = os.path.splitext(filename)
        ext = ext.lower()[1:]
        if not len(ext):
            filename = filename + '.png'
            ext = 'png'
        
        if ext=='png': type = 'png'
        elif ext in ('jpg', 'jpeg'): type = 'jpeg'
        else:
            error_msg_gtk('Can only save to formats png or jpeg')            
            return

        if not self._isRealized:
            self._printQued.append((filename, dpi))
            return

        origBounds = self.bbox.get_bounds()
        origDPI = self.dpi.get()
        dpi = 72.0/SCREENDPI*dpi

        self.dpi.set(dpi)        
        width = int(self.figsize[0]*dpi)
        height = int(self.figsize[1]*dpi)
        
        self.bbox.set_bounds(0, 0, width, height)


        for a in self.axes:
            a.wash_brushes()
            a.resize()

        for t in self._text:
            t.wash_brushes()

        gc = self.drawable.new_gc()
        gc.set_foreground('w')
        gdrawable = self.drawable.gdkDrawable
        ggc = gc.gdkGC
        
        pixmap = RendererGTK(gtk.gdk.Pixmap(gdrawable, width, height), self.dpi)

        pixmap.draw_rectangle(gc, True, 0, 0, width, height)

        #self.configure_event(self._drawingArea, 'configure')
        for ax in self.axes: ax.draw(pixmap)
        for t in self._text: t.draw(pixmap)
             
        pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, 0, 8, width, height)
        pixbuf.get_from_drawable(pixmap.gdkDrawable, gdrawable.get_colormap(),
                                 0, 0, 0, 0, width, height)

        try: pixbuf.save(filename, type)
        except gobject.GError, msg:
            self.bbox.set_bounds(*origBounds)
            self.dpi.set(origDPI)
            self.configure_event(self._drawingArea, 'configure')
            if self._doplot: self.draw()
            msg = raise_msg_to_str(msg)
            # note the error must be displayed here because trapping
            # the error on a call or print_figure may not work because
            # printing can be qued and called from realize
            error_msg_gtk('Could not save figure to %s\n\n%s' % (
                filename, msg))
        else:
            # successful draw, reset
            self.bbox.set_bounds(*origBounds)
            self.dpi.set(origDPI)
            if self._doplot:
                self.configure_event(self._drawingArea, 'configure')
                self.draw()

    
    def _update_renderer(self, widget):
        self._drawingArea = widget
        colorManager.set_drawing_area(widget)
        self.drawable = RendererGTK(widget.window, self.dpi)
        for a in self.axes:
            a.set_renderer(self.drawable)
            a.set_child_attr('_drawingArea', widget)

        for t in self._text:
            t.set_renderer(self.drawable)
            t.set_child_attr('_drawingArea', widget)

    def realize(self, widget):
        if widget is None: return 
        self._update_renderer(widget)
        self._isRealized = True
        for fname, dpi in self._printQued:
            self.print_figure(fname, dpi)
        self._printQued = []
        return gtk.TRUE


    def is_realized(self):
        return self._isRealized

    def text(self, x, y, s, *args, **kwargs):
        """
        Add text to figure at location x,y (relative 0-1 coords) See
        the help for Axis text for the meaning of the other arguments
        """

        override = _process_text_args({}, *args, **kwargs)
        t = AxisTextGTK(
            self.dpi, self.bbox,
            x=x, y=y, text=s,
            transx = Transform(Bound1D(0,1), self.bbox.x),
            transy = Transform(Bound1D(0,1), self.bbox.y),
            **override)
        t.set_child_attr('_drawingArea', self._drawingArea)
        self._text.append(t)
        return t





def raise_msg_to_str(msg):
    """msg is a return arg from a raise.  Join with new lines"""
    if not is_string_like(msg):
        msg = '\n'.join(map(str, msg))
    return msg
    
def error_msg_gtk(msg, parent=None):
    dialog = gtk.MessageDialog(
        parent         = None,
        type           = gtk.MESSAGE_ERROR,
        buttons        = gtk.BUTTONS_OK,
        message_format = msg)
    if parent is not None:
        dialog.set_transient_for(parent)
    dialog.show()
    dialog.run()
    dialog.destroy()
    return None


00504 class AxisTextGTK(AxisTextBase):
    """
    Handle storing and drawing of text in window or data coordinates
    """
    fontweights = {'normal' : pango.WEIGHT_NORMAL,
                   'bold' : pango.WEIGHT_BOLD,
                   'heavy' : pango.WEIGHT_HEAVY,
                   'light' : pango.WEIGHT_LIGHT,
                   'normal' : pango.WEIGHT_NORMAL,
                   'ultrabold' : pango.WEIGHT_ULTRABOLD,
                   'ultralight' : pango.WEIGHT_ULTRALIGHT,
                   }
    fontangles = {
        'italic': pango.STYLE_ITALIC,
        'normal' : pango.STYLE_NORMAL,
        'oblique' : pango.STYLE_OBLIQUE,
        }


    def __init__(self,
                 dpi,
                 bbox,
                 x=0, y=0, text='',
                 color='k',
                 verticalalignment='bottom',
                 horizontalalignment='left',
                 fontname='Sans',
                 fontsize=10,
                 fontweight='normal',
                 fontangle='normal',
                 rotation=None,
                 transx=Transform(), 
                 transy=Transform(),
                 ):
        AxisTextBase.__init__(self, dpi, bbox, x, y, text,
                 color, verticalalignment, horizontalalignment,
                 fontname, fontsize, fontweight, fontangle,
                 rotation, transx, transy,
                              )
        self._drawingArea = None # will be set by set_child_attr in figure

00545     def _compute_offsets(self):
        """
        Return the (x,y) offsets to adjust for the alignment
        specifications
        """
        if self._drawable and self._drawingArea is None: return 0,0
        try: self._layout
        except AttributeError: self._set_font()

        inkRect, logicalRect = self._layout.get_pixel_extents()
        rect = inkRect
        l, b, w, h = rect

        
        if self._rotation=='vertical':
            w, h = h, w
            l,b = b, l
            if self._horizontalalignment=='center': offsetx = -w/2
            elif self._horizontalalignment=='right': offsetx = -w
            else: offsetx = -b
            
            if self._verticalalignment=='center': offsety = -h/2-b
            elif self._verticalalignment=='top': offsety = 0
            else: offsety = -h
        else:
            if self._horizontalalignment=='center': offsetx = -w/2
            elif self._horizontalalignment=='right': offsetx = -w
            else: offsetx = 0

            if self._verticalalignment=='center': offsety = self._height/2
            elif self._verticalalignment=='top': offsety = 0
            else: offsety = self._height

        return (offsetx-l, offsety+b)


00581     def _draw(self, drawable, *args, **kwargs):
        """
        Render the text to the drawable (or defaul drawable is drawable is None
        """

        if (drawable is None and self._drawable is None): return 
        if drawable is not None: self._drawable = drawable
        
        if self._drawingArea is None:
            raise RuntimeError(
                'You must first set the _drawingArea for %s' % self.get_text())

        if self._text=='': return

        if self._reset or self.dpi.get() != self._lastDPI:
            self._set_font()

        x, y  = self._get_xy_display()
        ox, oy = self._compute_offsets()

        gc = drawable.new_gc()
        if self._clipOn:
            gc.set_clip_rectangle(self.bbox.get_bounds())
        gc.set_foreground(self._color)

        try: self._layout
        except AttributeError: self._set_font()

        if self._rotation=='vertical':            
            drawn = self.draw_rotated(drawable, gc)
            if not drawn: return 
        else:
            xox = int(x+ox)
            yoy = drawable.height - int(y+oy)

            drawable.gdkDrawable.draw_layout(
                gc.gdkGC, x=xox, y=yoy,
                layout=self._layout)

        self._reset = 0

00622     def draw_rotated(self, drawable, gc):
        """
        Draw the text rotated 90 degrees
        """

        gdrawable = drawable.gdkDrawable
        ggc = gc.gdkGC
        
        inkRect, logicalRect = self._layout.get_pixel_extents()
        #rect = inkRect
        rect = logicalRect
        w, h = int(rect[2]), int(rect[3])

        # get the background image
        x, y  = self._get_xy_display()
        ox, oy = self._compute_offsets()
        y = drawable.height-y

        xox = max(0,int(x+ox))
        yoy = max(0,int(y+oy))

        imgBack = gdrawable.get_image(xox, yoy, h, w)
        if imgBack is None: return 0

        pixmap = gtk.gdk.Pixmap(self._drawingArea.window, w, h)
        

        # rotate the background image to horizontal to fill the pixmap
        # background
        imageHoriz = gtk.gdk.Image(type=0,
                                   visual=pixmap.get_visual(),
                                   width=w, height=h)
        imageHoriz.set_colormap(imgBack.get_colormap())
        for i in range(w):
            for j in range(h):
                imageHoriz.put_pixel(i, j, imgBack.get_pixel(j,w-i-1) )
                



        pixmap.draw_image(ggc, imageHoriz, 0, 0, 0, 0, w, h)

        pixmap.draw_layout(ggc, x=0, y=0, layout=self._layout)
        imageIn = pixmap.get_image(x=0, y=0, width=w, height=h)
        imageOut = gtk.gdk.Image(type=0,
                                 visual=pixmap.get_visual(),
                                 width=h, height=w)
        imageOut.set_colormap(imageIn.get_colormap())
        for i in range(w):
            for j in range(h):
                imageOut.put_pixel(j, i, imageIn.get_pixel(w-i-1,j) )


        pixbuf = gtk.gdk.Pixbuf(colorspace=gtk.gdk.COLORSPACE_RGB,
                                has_alpha=0, bits_per_sample=8,
                                width=h, height=w)
        pixbuf.get_from_image(src=imageOut, cmap=imageIn.get_colormap(),
                              src_x=0, src_y=0, dest_x=0, dest_y=0,
                              width=h, height=w)

        pixbuf.render_to_drawable(gdrawable, ggc,
                                  src_x=0, src_y=0,
                                  dest_x=xox, dest_y=yoy,
                                  width=h, height=w,
                                  dither=0, x_dither=0, y_dither=0)
        return 1



        
    def get_window_extent(self):
        if self._reset or self.dpi.get() != self._lastDPI:
            self._set_font()
        
        inkRect, logicalRect = self._layout.get_pixel_extents()
        rect = inkRect
        l,b,w,h = rect
        x, y  = self._get_xy_display()
        ox, oy = self._compute_offsets()
        x = x+ox
        y = y+oy
        if self._rotation=='vertical':
            w, h = h, w
            left = x
            bottom = y
            left = x+b
        else:
            left = x+l
            bottom = y-h-b

        return Bound2D(left, bottom, w, h)
        
        
    def _set_font(self):
        "Update the pango layout"
        if self._drawingArea is None: return
        self._font = pango.FontDescription(
            '%s' % self._fontname)
        self._font.set_weight(self.fontweights[self._fontweight])
        self._font.set_style(self.fontangles[self._fontangle])
        scale = self.dpi.get()/72.0
        self._font.set_size(int(scale*self._fontsize*1024))
        self._context = self._drawingArea.create_pango_context()
        self._layout  = self._drawingArea.create_pango_layout(self._text)
        self._layout.set_font_description(self._font)    

        inkRect, logicalRect = self._layout.get_pixel_extents()
        rect = inkRect
        self._width = int(rect[2])
        self._height = int(rect[3])

        self._reset = False

        return 1


class ShowOn:
    show = 0
    _mainloopOn = 0
    __sharedState = {}

    def __init__(self):
        self.__dict__= self.__sharedState

    def set(self, on):
        # show all the fig wins
        
        if not self.show:
            for manager in Gcf.get_all_fig_managers():
                manager.window.show()
                manager.figure.draw()
            
        self.show = on

    def get(self):
        return self.show

    def enter_mainloop(self):
        self.set(1)
        self._mainloopOn = 1
        gtk.mainloop()

    def is_mainloop_on(self):
        return self._mainloopOn



def draw_if_interactive():
    if ShowOn().get():
        figManager =  Gcf.get_active()
        if figManager is not None:
            fig = figManager.figure
        fig.draw()
        fig.queue_draw()


def show():
    """
    Show all the figures and enter the gtk mainloop

    This should be the last line of your script
    """
    ShowOn().enter_mainloop()

def _quit_after_print_xvfb(*args):

    for manager in Gcf.get_all_fig_managers():
        if len(manager.figure._printQued): break
    else: gtk.mainquit()

def show_xvfb():
    """
    Print the pending figures only the quit, no screen draw
    """
    for manager in Gcf.get_all_fig_managers():
        manager.figure.set_do_plot(False)
        manager.window.show()
        
    gtk.idle_add(_quit_after_print_xvfb)
    gtk.mainloop()                

def new_figure_manager(num, figsize, dpi):
    """
    Create a new figure manager instance
    """
    
    thisFig = FigureGTK(figsize, dpi)
    thisFig.show()

    win = gtk.Window()
    win.set_title("Figure %d" % num)
    win.set_border_width(5)
    #def destroy(*args): Gcf.destroy(num)


    vbox = gtk.VBox(spacing=3)
    win.add(vbox)
    vbox.show()
    vbox.pack_start(thisFig)

    toolbar = NavigationToolbar( thisFig, win)
    toolbar.show()
    vbox.pack_start(toolbar, gtk.FALSE, gtk.FALSE )

    figManager = FigureManagerGTK(thisFig, num, win, vbox, toolbar)
    def destroy(*args): Gcf.destroy(num)
    win.connect("destroy", destroy)

    if ShowOn().get():
        win.show()

    return figManager

class FigureManagerGTK(FigureManagerBase):
    def __init__(self, figure, num, window, vbox, toolbar):
        FigureManagerBase.__init__(self, figure, num)

        self.window = window
        self.vbox = vbox
        self.toolbar = toolbar

    def add_subplot(self, *args, **kwargs):
        a = FigureManagerBase.add_subplot(self, *args, **kwargs)
        self.toolbar.update()
        return a
    
    def add_axes(self, rect, axisbg):
        a = FigureManagerBase.add_axes(self, rect, axisbg)
        self.toolbar.update()
        return a
    
    def set_current_axes(self, a):
        if a not in self.axes.values():
            error_msg_gtk('Axes is not in current figure')
        FigureManagerBase.set_current_axes(self, a)

    def destroy(self, *args):
        self.window.destroy()
        if Gcf.get_num_fig_managers()==0 and ShowOn().is_mainloop_on():
            gtk.mainquit()


        
class Dialog_MeasureTool(gtk.Dialog):
    def __init__(self):
        gtk.Dialog.__init__(self)
        self.set_title("Axis measurement tool")
        self.vbox.set_spacing(1)
        tooltips = gtk.Tooltips()

        self.posFmt =   'Position: x=%1.4f y=%1.4f'
        self.deltaFmt = 'Delta   : x=%1.4f y=%1.4f'

        self.positionLabel = gtk.Label(self.posFmt % (0,0))
        self.vbox.pack_start(self.positionLabel)
        self.positionLabel.show()
        tooltips.set_tip(self.positionLabel,
                         "Move the mouse to data point over axis")

        self.deltaLabel = gtk.Label(self.deltaFmt % (0,0))
        self.vbox.pack_start(self.deltaLabel)
        self.deltaLabel.show()

        tip = "Left click and hold while dragging mouse to measure " + \
              "delta x and delta y"
        tooltips.set_tip(self.deltaLabel, tip)
                         
        self.show()

    def update_position(self, x, y):
        self.positionLabel.set_text(self.posFmt % (x,y))

    def update_delta(self, dx, dy):
        self.deltaLabel.set_text(self.deltaFmt % (dx,dy))


class NavigationToolbar(gtk.Toolbar):
    
    def __init__(self, figure, win=None):
        """
        figure is the Figure instance that the toolboar controls

        win, if not None, is the gtk.Window the Figure is embedded in
        
        """
        gtk.Toolbar.__init__(self)
        self.win = win
        self.figure = figure
        iconSize = gtk.ICON_SIZE_SMALL_TOOLBAR
        self.set_border_width(5)
        self.set_style(gtk.TOOLBAR_ICONS)


        iconw = gtk.Image()
        iconw.set_from_stock(gtk.STOCK_GO_BACK, iconSize)
        self.bLeft = self.append_item(
            'Left',
            'Pan left with click or wheel mouse (bidirectional)',
            'Private',
            iconw,
            self.panx,
            -1)
        self.bLeft.connect("scroll_event", self.panx)

        iconw = gtk.Image()
        iconw.set_from_stock(gtk.STOCK_GO_FORWARD, iconSize)
        self.bRight = self.append_item(
            'Right',
            'Pan right with click or wheel mouse (bidirectional)',
            'Private',
            iconw,
            self.panx,
            1)
        self.bRight.connect("scroll_event", self.panx)

        iconw = gtk.Image()
        iconw.set_from_stock(gtk.STOCK_ZOOM_IN, iconSize)
        self.bZoomInX = self.append_item(
            'Zoom In X',
            'Zoom in X (shrink the x axis limits) with click or wheel mouse (bidirectional)',
            'Private',
            iconw,
            self.zoomx,
            1)
        self.bZoomInX.connect("scroll_event", self.zoomx)

        iconw = gtk.Image()
        iconw.set_from_stock(gtk.STOCK_ZOOM_OUT, iconSize)
        self.bZoomOutX = self.append_item(
            'Zoom Out X',
            'Zoom Out X (expand the x axis limits) with click or wheel mouse (bidirectional)',
            'Private',
            iconw,
            self.zoomx,
            -1)
        self.bZoomOutX.connect("scroll_event", self.zoomx)

        self.append_space()
        
        iconw = gtk.Image()
        iconw.set_from_stock(gtk.STOCK_GO_UP, iconSize)
        self.bUp = self.append_item(
            'Up',
            'Pan up with click or wheel mouse (bidirectional)',
            'Private',
            iconw,
            self.pany,
            1)
        self.bUp.connect("scroll_event", self.pany)


        iconw = gtk.Image()
        iconw.set_from_stock(gtk.STOCK_GO_DOWN, iconSize)
        self.bDown = self.append_item(
            'Down',
            'Pan down with click or wheel mouse (bidirectional)',
            'Private',
            iconw,
            self.pany,
            -1)
        self.bDown.connect("scroll_event", self.pany)

        iconw = gtk.Image()
        iconw.set_from_stock(gtk.STOCK_ZOOM_IN, iconSize)
        self.bZoomInY = self.append_item(
            'Zoom In Y',
            'Zoom in Y (shrink the y axis limits) with click or wheel mouse (bidirectional)',
            'Private',
            iconw,
            self.zoomy,
            1)
        self.bZoomInY.connect("scroll_event", self.zoomy)

        iconw = gtk.Image()
        iconw.set_from_stock(gtk.STOCK_ZOOM_OUT, iconSize)
        self.bZoomOutY = self.append_item(
            'Zoom Out Y',
            'Zoom Out Y (expand the y axis limits) with click or wheel mouse (bidirectional)',
            'Private',
            iconw,
            self.zoomy,
            -1)
        self.bZoomOutY.connect("scroll_event", self.zoomy)

        self.append_space()


        iconw = gtk.Image()
        iconw.set_from_stock(gtk.STOCK_SAVE, iconSize)
        self.bSave = self.append_item(
            'Save',
            'Save the figure',
            'Private',
            iconw,
            self.save_figure)

        self.append_space()


        def destroy_clicked(button):
            if win is not None: win.destroy()
            else: gtk.mainquit()
        iconw = gtk.Image()
        iconw.set_from_stock(gtk.STOCK_QUIT, iconSize)
        self.bQuit = self.append_item(
            'Close',
            'Close the figure',
            'Private',
            iconw,
            destroy_clicked)

        self.append_space()

        self.update()
    def make_axis_menu(self):

        def toggled(item, label):
            if item==itemAll:
                for item in items: item.set_active(1)
            elif item==itemInvert:
                for item in items:
                    item.set_active(not item.get_active())

            ind = [i for i,item in enumerate(items) if item.get_active()]
            self.set_active(ind)


                        
            
        menu = gtk.Menu()

        label = "All"
        itemAll = gtk.MenuItem(label)
        menu.append(itemAll)
        itemAll.connect("activate", toggled, label)
        itemAll.show()

        label = "Invert"
        itemInvert = gtk.MenuItem(label)
        menu.append(itemInvert)
        itemInvert.connect("activate", toggled, label)
        itemInvert.show()

        items = []
        for i in range(len(self._axes)):
            
            label = "Axis %d" % (i+1)
            item = gtk.CheckMenuItem(label)
            menu.append(item)
            item.connect("toggled", toggled, label)
            item.show()
            item.set_active(1)
            items.append(item)

        return menu

    def set_active(self, ind):
        self._ind = ind
        self._active = [ self._axes[i] for i in self._ind ]
        #for a in self._axes:
        #    a.wash_brushes()
        #self.figure.draw()
        
    def panx(self, button, arg):
        try: arg.direction
        except AttributeError: direction = arg
        else:
            if arg.direction == gdk.SCROLL_UP: direction=1
            else: direction=-1

        for a in self._active:
            a.panx(direction)
        self.figure.draw()
        return gtk.TRUE
    
    def pany(self, button, arg):
        try: arg.direction
        except AttributeError: direction = arg
        else:
            if arg.direction == gdk.SCROLL_UP: direction=1
            else: direction=-1
        for a in self._active:
            a.pany(direction)
        self.figure.draw()
        return gtk.TRUE
    
    def zoomx(self, button, arg):
        try: arg.direction
        except AttributeError: direction = arg
        else:            
            if arg.direction == gdk.SCROLL_UP: direction=1
            else: direction=-1

        for a in self._active:
            a.zoomx(direction)
        self.figure.draw()
        return gtk.TRUE

    def zoomy(self, button, arg):
        try: arg.direction
        except AttributeError: direction = arg
        else:
            if arg.direction == gdk.SCROLL_UP: direction=1
            else: direction=-1

        for a in self._active:
            a.zoomy(direction)
        self.figure.draw()
        return gtk.TRUE

    def menu_clicked(self, button):
        if event.button==3:
            self._axisMenu.popup(None, None, None, 0, 0)


    def save_figure(self, button):
                
        def print_ok(button):
            fname = fs.get_filename()
            self.lastDir = os.path.dirname(fname)
            fs.destroy()
            try: self.figure.print_figure(fname)
            except IOError, msg:                
                err = '\n'.join(map(str, msg))
                msg = 'Failed to save %s: Error msg was\n\n%s' % (
                    fname, err)
            
        fs = gtk.FileSelection(title='Save the figure')
        if self.win is not None:
            fs.set_transient_for(self.win)
        try: self.lastDir
        except AttributeError: pass
        else: fs.set_filename(self.lastDir + os.sep)

        fs.ok_button.connect("clicked", print_ok)
        fs.cancel_button.connect("clicked", lambda b: fs.destroy())
        fs.show()


    def update(self):
        self._axes = self.figure.get_axes()
        self.set_active(range(len(self._axes)))
        if len(self._axes)>1:

            try: self.omenu
            except AttributeError:
                self.omenu = gtk.OptionMenu()
                self.omenu.set_border_width(3)
                self.omenu.show()
                self.insert_widget(
                    self.omenu,
                    'Select axes that controls affect',
                    'Private', 0)
                
            # set up the axis menu
            menu = self.make_axis_menu()
            self.omenu.set_menu(menu)
        self.set_active(range(len(self._axes)))

FigureManager = FigureManagerBase
AxisText = AxisTextGTK
Figure = FigureGTK
error_msg = error_msg_gtk

Generated by  Doxygen 1.6.0   Back to index