Пример #1
0
 def _renderer_init(self):
     """Override by GTK backends to select a different renderer
     Renderer should provide the methods:
         set_pixmap ()
         set_width_height ()
     that are used by
         _render_figure() / _pixmap_prepare()
     """
     self._renderer = RendererGDK(self, self.figure.dpi)
Пример #2
0
 def _renderer_init(self):
     """Override by GTK backends to select a different renderer
     Renderer should provide the methods:
         set_pixmap ()
         set_width_height ()
     that are used by
         _render_figure() / _pixmap_prepare()
     """
     self._renderer = RendererGDK (self, self.figure.dpi)
Пример #3
0
class FigureCanvasGTK(gtk.DrawingArea, FigureCanvasBase):
    keyvald = {
        65507: 'control',
        65505: 'shift',
        65513: 'alt',
        65508: 'control',
        65506: 'shift',
        65514: 'alt',
        65361: 'left',
        65362: 'up',
        65363: 'right',
        65364: 'down',
        65307: 'escape',
        65470: 'f1',
        65471: 'f2',
        65472: 'f3',
        65473: 'f4',
        65474: 'f5',
        65475: 'f6',
        65476: 'f7',
        65477: 'f8',
        65478: 'f9',
        65479: 'f10',
        65480: 'f11',
        65481: 'f12',
        65300: 'scroll_lock',
        65299: 'break',
        65288: 'backspace',
        65293: 'enter',
        65379: 'insert',
        65535: 'delete',
        65360: 'home',
        65367: 'end',
        65365: 'pageup',
        65366: 'pagedown',
        65438: '0',
        65436: '1',
        65433: '2',
        65435: '3',
        65430: '4',
        65437: '5',
        65432: '6',
        65429: '7',
        65431: '8',
        65434: '9',
        65451: '+',
        65453: '-',
        65450: '*',
        65455: '/',
        65439: 'dec',
        65421: 'enter',
    }

    # Setting this as a static constant prevents
    # this resulting expression from leaking
    event_mask = (gdk.BUTTON_PRESS_MASK | gdk.BUTTON_RELEASE_MASK
                  | gdk.EXPOSURE_MASK | gdk.KEY_PRESS_MASK
                  | gdk.KEY_RELEASE_MASK | gdk.ENTER_NOTIFY_MASK
                  | gdk.LEAVE_NOTIFY_MASK | gdk.POINTER_MOTION_MASK
                  | gdk.POINTER_MOTION_HINT_MASK)

    def __init__(self, figure):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        FigureCanvasBase.__init__(self, figure)
        gtk.DrawingArea.__init__(self)

        self._idle_draw_id = 0
        self._need_redraw = True
        self._pixmap_width = -1
        self._pixmap_height = -1
        self._lastCursor = None

        self.connect('scroll_event', self.scroll_event)
        self.connect('button_press_event', self.button_press_event)
        self.connect('button_release_event', self.button_release_event)
        self.connect('configure_event', self.configure_event)
        self.connect('expose_event', self.expose_event)
        self.connect('key_press_event', self.key_press_event)
        self.connect('key_release_event', self.key_release_event)
        self.connect('motion_notify_event', self.motion_notify_event)
        self.connect('leave_notify_event', self.leave_notify_event)
        self.connect('enter_notify_event', self.enter_notify_event)

        self.set_events(self.__class__.event_mask)

        self.set_double_buffered(False)
        self.set_flags(gtk.CAN_FOCUS)
        self._renderer_init()

        self._idle_event_id = gobject.idle_add(self.idle_event)

    def destroy(self):
        #gtk.DrawingArea.destroy(self)
        gobject.source_remove(self._idle_event_id)
        if self._idle_draw_id != 0:
            gobject.source_remove(self._idle_draw_id)

    def scroll_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        if event.direction == gdk.SCROLL_UP:
            step = 1
        else:
            step = -1
        FigureCanvasBase.scroll_event(self, x, y, step, guiEvent=event)
        return False  # finish event propagation?

    def button_press_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        FigureCanvasBase.button_press_event(self,
                                            x,
                                            y,
                                            event.button,
                                            guiEvent=event)
        return False  # finish event propagation?

    def button_release_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        FigureCanvasBase.button_release_event(self,
                                              x,
                                              y,
                                              event.button,
                                              guiEvent=event)
        return False  # finish event propagation?

    def key_press_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        key = self._get_key(event)
        if _debug: print "hit", key
        FigureCanvasBase.key_press_event(self, key, guiEvent=event)
        return False  # finish event propagation?

    def key_release_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        key = self._get_key(event)
        if _debug: print "release", key
        FigureCanvasBase.key_release_event(self, key, guiEvent=event)
        return False  # finish event propagation?

    def motion_notify_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        if event.is_hint:
            x, y, state = event.window.get_pointer()
        else:
            x, y, state = event.x, event.y, event.state

        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - y
        FigureCanvasBase.motion_notify_event(self, x, y, guiEvent=event)
        return False  # finish event propagation?

    def leave_notify_event(self, widget, event):
        FigureCanvasBase.leave_notify_event(self, event)

    def enter_notify_event(self, widget, event):
        FigureCanvasBase.enter_notify_event(self, event)

    def _get_key(self, event):
        if event.keyval in self.keyvald:
            key = self.keyvald[event.keyval]
        elif event.keyval < 256:
            key = chr(event.keyval)
        else:
            key = None

        ctrl = event.state & gdk.CONTROL_MASK
        shift = event.state & gdk.SHIFT_MASK
        return key

    def configure_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        if widget.window is None:
            return
        w, h = event.width, event.height
        if w < 3 or h < 3:
            return  # empty fig

        # resize the figure (in inches)
        dpi = self.figure.dpi
        self.figure.set_size_inches(w / dpi, h / dpi)
        self._need_redraw = True

        return False  # finish event propagation?

    def draw(self):
        # Note: FigureCanvasBase.draw() is inconveniently named as it clashes
        # with the deprecated gtk.Widget.draw()

        self._need_redraw = True
        if GTK_WIDGET_DRAWABLE(self):
            self.queue_draw()
            # do a synchronous draw (its less efficient than an async draw,
            # but is required if/when animation is used)
            self.window.process_updates(False)

    def draw_idle(self):
        def idle_draw(*args):
            self.draw()
            self._idle_draw_id = 0
            return False

        if self._idle_draw_id == 0:
            self._idle_draw_id = gobject.idle_add(idle_draw)

    def _renderer_init(self):
        """Override by GTK backends to select a different renderer
        Renderer should provide the methods:
            set_pixmap ()
            set_width_height ()
        that are used by
            _render_figure() / _pixmap_prepare()
        """
        self._renderer = RendererGDK(self, self.figure.dpi)

    def _pixmap_prepare(self, width, height):
        """
        Make sure _._pixmap is at least width, height,
        create new pixmap if necessary
        """
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()

        create_pixmap = False
        if width > self._pixmap_width:
            # increase the pixmap in 10%+ (rather than 1 pixel) steps
            self._pixmap_width = max(int(self._pixmap_width * 1.1), width)
            create_pixmap = True

        if height > self._pixmap_height:
            self._pixmap_height = max(int(self._pixmap_height * 1.1), height)
            create_pixmap = True

        if create_pixmap:
            self._pixmap = gdk.Pixmap(self.window, self._pixmap_width,
                                      self._pixmap_height)
            self._renderer.set_pixmap(self._pixmap)

    def _render_figure(self, pixmap, width, height):
        """used by GTK and GTKcairo. GTKAgg overrides
        """
        self._renderer.set_width_height(width, height)
        self.figure.draw(self._renderer)

    def expose_event(self, widget, event):
        """Expose_event for all GTK backends. Should not be overridden.
        """
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()

        if GTK_WIDGET_DRAWABLE(self):
            if self._need_redraw:
                x, y, w, h = self.allocation
                self._pixmap_prepare(w, h)
                self._render_figure(self._pixmap, w, h)
                self._need_redraw = False

            x, y, w, h = event.area
            self.window.draw_drawable(self.style.fg_gc[self.state],
                                      self._pixmap, x, y, x, y, w, h)
        return False  # finish event propagation?

    filetypes = FigureCanvasBase.filetypes.copy()
    filetypes['jpg'] = 'JPEG'
    filetypes['jpeg'] = 'JPEG'
    filetypes['png'] = 'Portable Network Graphics'

    def print_jpeg(self, filename, *args, **kwargs):
        return self._print_image(filename, 'jpeg')

    print_jpg = print_jpeg

    def print_png(self, filename, *args, **kwargs):
        return self._print_image(filename, 'png')

    def _print_image(self, filename, format):
        if self.flags() & gtk.REALIZED == 0:
            # for self.window(for pixmap) and has a side effect of altering
            # figure width,height (via configure-event?)
            gtk.DrawingArea.realize(self)

        width, height = self.get_width_height()
        pixmap = gdk.Pixmap(self.window, width, height)
        self._renderer.set_pixmap(pixmap)
        self._render_figure(pixmap, width, height)

        # jpg colors don't match the display very well, png colors match
        # better
        pixbuf = gdk.Pixbuf(gdk.COLORSPACE_RGB, 0, 8, width, height)
        pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0,
                                 width, height)

        if is_string_like(filename):
            try:
                pixbuf.save(filename, format)
            except gobject.GError, exc:
                error_msg_gtk('Save figure failure:\n%s' % (exc, ),
                              parent=self)
        elif is_writable_file_like(filename):
            if hasattr(pixbuf, 'save_to_callback'):

                def save_callback(buf, data=None):
                    data.write(buf)

                try:
                    pixbuf.save_to_callback(save_callback,
                                            format,
                                            user_data=filename)
                except gobject.GError, exc:
                    error_msg_gtk('Save figure failure:\n%s' % (exc, ),
                                  parent=self)
Пример #4
0
class FigureCanvasGTK (gtk.DrawingArea, FigureCanvasBase):
    keyvald = {65507 : 'control',
               65505 : 'shift',
               65513 : 'alt',
               65508 : 'control',
               65506 : 'shift',
               65514 : 'alt',
               65361 : 'left',
               65362 : 'up',
               65363 : 'right',
               65364 : 'down',
               65307 : 'escape',
               65470 : 'f1',
               65471 : 'f2',
               65472 : 'f3',
               65473 : 'f4',
               65474 : 'f5',
               65475 : 'f6',
               65476 : 'f7',
               65477 : 'f8',
               65478 : 'f9',
               65479 : 'f10',
               65480 : 'f11',
               65481 : 'f12',
               65300 : 'scroll_lock',
               65299 : 'break',
               65288 : 'backspace',
               65293 : 'enter',
               65379 : 'insert',
               65535 : 'delete',
               65360 : 'home',
               65367 : 'end',
               65365 : 'pageup',
               65366 : 'pagedown',
               65438 : '0',
               65436 : '1',
               65433 : '2',
               65435 : '3',
               65430 : '4',
               65437 : '5',
               65432 : '6',
               65429 : '7',
               65431 : '8',
               65434 : '9',
               65451 : '+',
               65453 : '-',
               65450 : '*',
               65455 : '/',
               65439 : 'dec',
               65421 : 'enter',
               }

    # Setting this as a static constant prevents
    # this resulting expression from leaking
    event_mask = (gdk.BUTTON_PRESS_MASK   |
                  gdk.BUTTON_RELEASE_MASK |
                  gdk.EXPOSURE_MASK       |
                  gdk.KEY_PRESS_MASK      |
                  gdk.KEY_RELEASE_MASK    |
                  gdk.ENTER_NOTIFY_MASK   |
                  gdk.LEAVE_NOTIFY_MASK   |
                  gdk.POINTER_MOTION_MASK |
                  gdk.POINTER_MOTION_HINT_MASK)

    def __init__(self, figure):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        FigureCanvasBase.__init__(self, figure)
        gtk.DrawingArea.__init__(self)

        self._idle_draw_id  = 0
        self._need_redraw   = True
        self._pixmap_width  = -1
        self._pixmap_height = -1
        self._lastCursor    = None

        self.connect('scroll_event',         self.scroll_event)
        self.connect('button_press_event',   self.button_press_event)
        self.connect('button_release_event', self.button_release_event)
        self.connect('configure_event',      self.configure_event)
        self.connect('expose_event',         self.expose_event)
        self.connect('key_press_event',      self.key_press_event)
        self.connect('key_release_event',    self.key_release_event)
        self.connect('motion_notify_event',  self.motion_notify_event)
        self.connect('leave_notify_event',   self.leave_notify_event)
        self.connect('enter_notify_event',   self.enter_notify_event)

        self.set_events(self.__class__.event_mask)

        self.set_double_buffered(False)
        self.set_flags(gtk.CAN_FOCUS)
        self._renderer_init()

        self._idle_event_id = gobject.idle_add(self.idle_event)

    def destroy(self):
        #gtk.DrawingArea.destroy(self)
        gobject.source_remove(self._idle_event_id)
        if self._idle_draw_id != 0:
            gobject.source_remove(self._idle_draw_id)

    def scroll_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        if event.direction==gdk.SCROLL_UP:
            step = 1
        else:
            step = -1
        FigureCanvasBase.scroll_event(self, x, y, step, guiEvent=event)
        return False  # finish event propagation?

    def button_press_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        FigureCanvasBase.button_press_event(self, x, y, event.button, guiEvent=event)
        return False  # finish event propagation?

    def button_release_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        FigureCanvasBase.button_release_event(self, x, y, event.button, guiEvent=event)
        return False  # finish event propagation?

    def key_press_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        key = self._get_key(event)
        if _debug: print "hit", key
        FigureCanvasBase.key_press_event(self, key, guiEvent=event)
        return False  # finish event propagation?

    def key_release_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        key = self._get_key(event)
        if _debug: print "release", key
        FigureCanvasBase.key_release_event(self, key, guiEvent=event)
        return False  # finish event propagation?

    def motion_notify_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        if event.is_hint:
            x, y, state = event.window.get_pointer()
        else:
            x, y, state = event.x, event.y, event.state

        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - y
        FigureCanvasBase.motion_notify_event(self, x, y, guiEvent=event)
        return False  # finish event propagation?

    def leave_notify_event(self, widget, event):
        FigureCanvasBase.leave_notify_event(self, event)

    def enter_notify_event(self, widget, event):
        FigureCanvasBase.enter_notify_event(self, event)

    def _get_key(self, event):
        if event.keyval in self.keyvald:
            key = self.keyvald[event.keyval]
        elif event.keyval <256:
            key = chr(event.keyval)
        else:
            key = None

        ctrl  = event.state & gdk.CONTROL_MASK
        shift = event.state & gdk.SHIFT_MASK
        return key


    def configure_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        if widget.window is None:
            return
        w, h = event.width, event.height
        if w < 3 or h < 3:
            return # empty fig

        # resize the figure (in inches)
        dpi = self.figure.dpi
        self.figure.set_size_inches (w/dpi, h/dpi)
        self._need_redraw = True

        return False  # finish event propagation?


    def draw(self):
        # Note: FigureCanvasBase.draw() is inconveniently named as it clashes
        # with the deprecated gtk.Widget.draw()

        self._need_redraw = True
        if GTK_WIDGET_DRAWABLE(self):
            self.queue_draw()
            # do a synchronous draw (its less efficient than an async draw,
            # but is required if/when animation is used)
            self.window.process_updates (False)

    def draw_idle(self):
        def idle_draw(*args):
            self.draw()
            self._idle_draw_id = 0
            return False
        if self._idle_draw_id == 0:
            self._idle_draw_id = gobject.idle_add(idle_draw)


    def _renderer_init(self):
        """Override by GTK backends to select a different renderer
        Renderer should provide the methods:
            set_pixmap ()
            set_width_height ()
        that are used by
            _render_figure() / _pixmap_prepare()
        """
        self._renderer = RendererGDK (self, self.figure.dpi)


    def _pixmap_prepare(self, width, height):
        """
        Make sure _._pixmap is at least width, height,
        create new pixmap if necessary
        """
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()

        create_pixmap = False
        if width > self._pixmap_width:
            # increase the pixmap in 10%+ (rather than 1 pixel) steps
            self._pixmap_width  = max (int (self._pixmap_width  * 1.1),
                                       width)
            create_pixmap = True

        if height > self._pixmap_height:
            self._pixmap_height = max (int (self._pixmap_height * 1.1),
                                           height)
            create_pixmap = True

        if create_pixmap:
            self._pixmap = gdk.Pixmap (self.window, self._pixmap_width,
                                       self._pixmap_height)
            self._renderer.set_pixmap (self._pixmap)


    def _render_figure(self, pixmap, width, height):
        """used by GTK and GTKcairo. GTKAgg overrides
        """
        self._renderer.set_width_height (width, height)
        self.figure.draw (self._renderer)


    def expose_event(self, widget, event):
        """Expose_event for all GTK backends. Should not be overridden.
        """
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()

        if GTK_WIDGET_DRAWABLE(self):
            if self._need_redraw:
                x, y, w, h = self.allocation
                self._pixmap_prepare (w, h)
                self._render_figure(self._pixmap, w, h)
                self._need_redraw = False

            x, y, w, h = event.area
            self.window.draw_drawable (self.style.fg_gc[self.state],
                                       self._pixmap, x, y, x, y, w, h)
        return False  # finish event propagation?

    filetypes = FigureCanvasBase.filetypes.copy()
    filetypes['jpg'] = 'JPEG'
    filetypes['jpeg'] = 'JPEG'
    filetypes['png'] = 'Portable Network Graphics'

    def print_jpeg(self, filename, *args, **kwargs):
        return self._print_image(filename, 'jpeg')
    print_jpg = print_jpeg

    def print_png(self, filename, *args, **kwargs):
        return self._print_image(filename, 'png')

    def _print_image(self, filename, format):
        if self.flags() & gtk.REALIZED == 0:
            # for self.window(for pixmap) and has a side effect of altering
            # figure width,height (via configure-event?)
            gtk.DrawingArea.realize(self)

        width, height = self.get_width_height()
        pixmap = gdk.Pixmap (self.window, width, height)
        self._renderer.set_pixmap (pixmap)
        self._render_figure(pixmap, width, height)

        # jpg colors don't match the display very well, png colors match
        # better
        pixbuf = gdk.Pixbuf(gdk.COLORSPACE_RGB, 0, 8, width, height)
        pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(),
                                     0, 0, 0, 0, width, height)

        if is_string_like(filename):
            try:
                pixbuf.save(filename, format)
            except gobject.GError, exc:
                error_msg_gtk('Save figure failure:\n%s' % (exc,), parent=self)
        elif is_writable_file_like(filename):
            if hasattr(pixbuf, 'save_to_callback'):
                def save_callback(buf, data=None):
                    data.write(buf)
                try:
                    pixbuf.save_to_callback(save_callback, format, user_data=filename)
                except gobject.GError, exc:
                    error_msg_gtk('Save figure failure:\n%s' % (exc,), parent=self)
Пример #5
0
class FigureCanvasGTK(gtk.DrawingArea, FigureCanvasBase):
    keyvald = {
        65507: 'control',
        65505: 'shift',
        65513: 'alt',
        65508: 'control',
        65506: 'shift',
        65514: 'alt',
        65361: 'left',
        65362: 'up',
        65363: 'right',
        65364: 'down',
        65307: 'escape',
        65470: 'f1',
        65471: 'f2',
        65472: 'f3',
        65473: 'f4',
        65474: 'f5',
        65475: 'f6',
        65476: 'f7',
        65477: 'f8',
        65478: 'f9',
        65479: 'f10',
        65480: 'f11',
        65481: 'f12',
        65300: 'scroll_lock',
        65299: 'break',
        65288: 'backspace',
        65293: 'enter',
        65379: 'insert',
        65535: 'delete',
        65360: 'home',
        65367: 'end',
        65365: 'pageup',
        65366: 'pagedown',
        65438: '0',
        65436: '1',
        65433: '2',
        65435: '3',
        65430: '4',
        65437: '5',
        65432: '6',
        65429: '7',
        65431: '8',
        65434: '9',
        65451: '+',
        65453: '-',
        65450: '*',
        65455: '/',
        65439: 'dec',
        65421: 'enter',
        65511: 'super',
        65512: 'super',
        65406: 'alt',
        65289: 'tab',
    }

    # Setting this as a static constant prevents
    # this resulting expression from leaking
    event_mask = (gdk.BUTTON_PRESS_MASK | gdk.BUTTON_RELEASE_MASK
                  | gdk.EXPOSURE_MASK | gdk.KEY_PRESS_MASK
                  | gdk.KEY_RELEASE_MASK | gdk.ENTER_NOTIFY_MASK
                  | gdk.LEAVE_NOTIFY_MASK | gdk.POINTER_MOTION_MASK
                  | gdk.POINTER_MOTION_HINT_MASK)

    def __init__(self, figure):
        if self.__class__ == matplotlib.backends.backend_gtk.FigureCanvasGTK:
            warn_deprecated('2.0',
                            message="The GTK backend is "
                            "deprecated. It is untested, known to be "
                            "broken and will be removed in Matplotlib 2.2. "
                            "Use the GTKAgg backend instead. "
                            "See Matplotlib usage FAQ for"
                            " more info on backends.",
                            alternative="GTKAgg")
        FigureCanvasBase.__init__(self, figure)
        gtk.DrawingArea.__init__(self)

        self._idle_draw_id = 0
        self._need_redraw = True
        self._pixmap_width = -1
        self._pixmap_height = -1
        self._lastCursor = None

        self.connect('scroll_event', self.scroll_event)
        self.connect('button_press_event', self.button_press_event)
        self.connect('button_release_event', self.button_release_event)
        self.connect('configure_event', self.configure_event)
        self.connect('expose_event', self.expose_event)
        self.connect('key_press_event', self.key_press_event)
        self.connect('key_release_event', self.key_release_event)
        self.connect('motion_notify_event', self.motion_notify_event)
        self.connect('leave_notify_event', self.leave_notify_event)
        self.connect('enter_notify_event', self.enter_notify_event)

        self.set_events(self.__class__.event_mask)

        self.set_double_buffered(False)
        self.set_flags(gtk.CAN_FOCUS)
        self._renderer_init()

        self.last_downclick = {}

    def destroy(self):
        #gtk.DrawingArea.destroy(self)
        self.close_event()
        if self._idle_draw_id != 0:
            gobject.source_remove(self._idle_draw_id)

    def scroll_event(self, widget, event):
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        if event.direction == gdk.SCROLL_UP:
            step = 1
        else:
            step = -1
        FigureCanvasBase.scroll_event(self, x, y, step, guiEvent=event)
        return False  # finish event propagation?

    def button_press_event(self, widget, event):
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        dblclick = (event.type == gdk._2BUTTON_PRESS)
        if not dblclick:
            # GTK is the only backend that generates a DOWN-UP-DOWN-DBLCLICK-UP  event
            # sequence for a double click.  All other backends have a DOWN-UP-DBLCLICK-UP
            # sequence.  In order to provide consistency to matplotlib users, we will
            # eat the extra DOWN event in the case that we detect it is part of a double
            # click.
            # first, get the double click time in milliseconds.
            current_time = event.get_time()
            last_time = self.last_downclick.get(event.button, 0)
            dblclick_time = gtk.settings_get_for_screen(
                gdk.screen_get_default()).get_property('gtk-double-click-time')
            delta_time = current_time - last_time
            if delta_time < dblclick_time:
                del self.last_downclick[
                    event.button]  # we do not want to eat more than one event.
                return False  # eat.
            self.last_downclick[event.button] = current_time
        FigureCanvasBase.button_press_event(self,
                                            x,
                                            y,
                                            event.button,
                                            dblclick=dblclick,
                                            guiEvent=event)
        return False  # finish event propagation?

    def button_release_event(self, widget, event):
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        FigureCanvasBase.button_release_event(self,
                                              x,
                                              y,
                                              event.button,
                                              guiEvent=event)
        return False  # finish event propagation?

    def key_press_event(self, widget, event):
        key = self._get_key(event)
        FigureCanvasBase.key_press_event(self, key, guiEvent=event)
        return True  # stop event propagation

    def key_release_event(self, widget, event):
        key = self._get_key(event)
        FigureCanvasBase.key_release_event(self, key, guiEvent=event)
        return True  # stop event propagation

    def motion_notify_event(self, widget, event):
        if event.is_hint:
            x, y, state = event.window.get_pointer()
        else:
            x, y, state = event.x, event.y, event.state

        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - y
        FigureCanvasBase.motion_notify_event(self, x, y, guiEvent=event)
        return False  # finish event propagation?

    def leave_notify_event(self, widget, event):
        FigureCanvasBase.leave_notify_event(self, event)

    def enter_notify_event(self, widget, event):
        x, y, state = event.window.get_pointer()
        FigureCanvasBase.enter_notify_event(self, event, xy=(x, y))

    def _get_key(self, event):
        if event.keyval in self.keyvald:
            key = self.keyvald[event.keyval]
        elif event.keyval < 256:
            key = chr(event.keyval)
        else:
            key = None

        for key_mask, prefix in (
            [gdk.MOD4_MASK, 'super'],
            [gdk.MOD1_MASK, 'alt'],
            [gdk.CONTROL_MASK, 'ctrl'],
        ):
            if event.state & key_mask:
                key = '{0}+{1}'.format(prefix, key)

        return key

    def configure_event(self, widget, event):
        if widget.window is None:
            return
        w, h = event.width, event.height
        if w < 3 or h < 3:
            return  # empty fig

        # resize the figure (in inches)
        dpi = self.figure.dpi
        self.figure.set_size_inches(w / dpi, h / dpi, forward=False)
        self._need_redraw = True

        return False  # finish event propagation?

    def draw(self):
        # Note: FigureCanvasBase.draw() is inconveniently named as it clashes
        # with the deprecated gtk.Widget.draw()

        self._need_redraw = True
        if GTK_WIDGET_DRAWABLE(self):
            self.queue_draw()
            # do a synchronous draw (its less efficient than an async draw,
            # but is required if/when animation is used)
            self.window.process_updates(False)

    def draw_idle(self):
        if self._idle_draw_id != 0:
            return

        def idle_draw(*args):
            try:
                self.draw()
            finally:
                self._idle_draw_id = 0
            return False

        self._idle_draw_id = gobject.idle_add(idle_draw)

    def _renderer_init(self):
        """Override by GTK backends to select a different renderer
        Renderer should provide the methods:
            set_pixmap ()
            set_width_height ()
        that are used by
            _render_figure() / _pixmap_prepare()
        """
        self._renderer = RendererGDK(self, self.figure.dpi)

    def _pixmap_prepare(self, width, height):
        """
        Make sure _._pixmap is at least width, height,
        create new pixmap if necessary
        """
        create_pixmap = False
        if width > self._pixmap_width:
            # increase the pixmap in 10%+ (rather than 1 pixel) steps
            self._pixmap_width = max(int(self._pixmap_width * 1.1), width)
            create_pixmap = True

        if height > self._pixmap_height:
            self._pixmap_height = max(int(self._pixmap_height * 1.1), height)
            create_pixmap = True

        if create_pixmap:
            self._pixmap = gdk.Pixmap(self.window, self._pixmap_width,
                                      self._pixmap_height)
            self._renderer.set_pixmap(self._pixmap)

    def _render_figure(self, pixmap, width, height):
        """used by GTK and GTKcairo. GTKAgg overrides
        """
        self._renderer.set_width_height(width, height)
        self.figure.draw(self._renderer)

    def expose_event(self, widget, event):
        """Expose_event for all GTK backends. Should not be overridden.
        """
        toolbar = self.toolbar
        if toolbar:
            toolbar.set_cursor(cursors.WAIT)
        if GTK_WIDGET_DRAWABLE(self):
            if self._need_redraw:
                x, y, w, h = self.allocation
                self._pixmap_prepare(w, h)
                self._render_figure(self._pixmap, w, h)
                self._need_redraw = False
            x, y, w, h = event.area
            self.window.draw_drawable(self.style.fg_gc[self.state],
                                      self._pixmap, x, y, x, y, w, h)
        if toolbar:
            toolbar.set_cursor(toolbar._lastCursor)
        return False  # finish event propagation?

    filetypes = FigureCanvasBase.filetypes.copy()
    filetypes['jpg'] = 'JPEG'
    filetypes['jpeg'] = 'JPEG'
    filetypes['png'] = 'Portable Network Graphics'

    def print_jpeg(self, filename, *args, **kwargs):
        return self._print_image(filename, 'jpeg')

    print_jpg = print_jpeg

    def print_png(self, filename, *args, **kwargs):
        return self._print_image(filename, 'png')

    def _print_image(self, filename, format, *args, **kwargs):
        if self.flags() & gtk.REALIZED == 0:
            # for self.window(for pixmap) and has a side effect of altering
            # figure width,height (via configure-event?)
            gtk.DrawingArea.realize(self)

        width, height = self.get_width_height()
        pixmap = gdk.Pixmap(self.window, width, height)
        self._renderer.set_pixmap(pixmap)
        self._render_figure(pixmap, width, height)

        # jpg colors don't match the display very well, png colors match
        # better
        pixbuf = gdk.Pixbuf(gdk.COLORSPACE_RGB, 0, 8, width, height)
        pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0,
                                 width, height)

        # set the default quality, if we are writing a JPEG.
        # http://www.pygtk.org/docs/pygtk/class-gdkpixbuf.html#method-gdkpixbuf--save
        options = {k: kwargs[k] for k in ['quality'] if k in kwargs}
        if format in ['jpg', 'jpeg']:
            options.setdefault('quality', rcParams['savefig.jpeg_quality'])
            options['quality'] = str(options['quality'])

        if isinstance(filename, six.string_types):
            try:
                pixbuf.save(filename, format, options=options)
            except gobject.GError as exc:
                error_msg_gtk('Save figure failure:\n%s' % (exc, ),
                              parent=self)
        elif is_writable_file_like(filename):
            if hasattr(pixbuf, 'save_to_callback'):

                def save_callback(buf, data=None):
                    data.write(buf)

                try:
                    pixbuf.save_to_callback(save_callback,
                                            format,
                                            user_data=filename,
                                            options=options)
                except gobject.GError as exc:
                    error_msg_gtk('Save figure failure:\n%s' % (exc, ),
                                  parent=self)
            else:
                raise ValueError(
                    "Saving to a Python file-like object is only supported by PyGTK >= 2.8"
                )
        else:
            raise ValueError("filename must be a path or a file-like object")

    def new_timer(self, *args, **kwargs):
        """
        Creates a new backend-specific subclass of :class:`backend_bases.Timer`.
        This is useful for getting periodic events through the backend's native
        event loop. Implemented only for backends with GUIs.

        Other Parameters
        ----------------
        interval : scalar
            Timer interval in milliseconds
        callbacks : list
            Sequence of (func, args, kwargs) where ``func(*args, **kwargs)``
            will be executed by the timer every *interval*.
        """
        return TimerGTK(*args, **kwargs)

    def flush_events(self):
        gtk.gdk.threads_enter()
        while gtk.events_pending():
            gtk.main_iteration(True)
        gtk.gdk.flush()
        gtk.gdk.threads_leave()
Пример #6
0
class FigureCanvasGTK(gtk.DrawingArea, FigureCanvasBase):
    keyvald = {
        65507: 'control',
        65505: 'shift',
        65513: 'alt',
        65508: 'control',
        65506: 'shift',
        65514: 'alt',
        65361: 'left',
        65362: 'up',
        65363: 'right',
        65364: 'down',
    }

    def __init__(self, figure):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        FigureCanvasBase.__init__(self, figure)
        gtk.DrawingArea.__init__(self)

        self._idleID = 0
        self._need_redraw = True
        self._pixmap_width = -1
        self._pixmap_height = -1
        self._lastCursor = None

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

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

        self.set_double_buffered(False)
        self.set_flags(gtk.CAN_FOCUS)
        self._renderer_init()

    def button_press_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        FigureCanvasBase.button_press_event(self, x, y, event.button)
        return False  # finish event propagation?

    def button_release_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        FigureCanvasBase.button_release_event(self, x, y, event.button)
        return False  # finish event propagation?

    def key_press_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        key = self._get_key(event)
        if _debug: print "hit", key
        FigureCanvasBase.key_press_event(self, key)
        return False  # finish event propagation?

    def key_release_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        key = self._get_key(event)
        if _debug: print "release", key
        FigureCanvasBase.key_release_event(self, key)
        return False  # finish event propagation?

    def motion_notify_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        if event.is_hint:
            x, y, state = event.window.get_pointer()
        else:
            x, y, state = event.x, event.y, event.state

        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - y
        FigureCanvasBase.motion_notify_event(self, x, y)
        return False  # finish event propagation?

    def _get_key(self, event):
        if self.keyvald.has_key(event.keyval):
            key = self.keyvald[event.keyval]
        elif event.keyval < 256:
            key = chr(event.keyval)
        else:
            key = None

        ctrl = event.state & gdk.CONTROL_MASK
        shift = event.state & gdk.SHIFT_MASK
        return key

    def configure_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        if widget.window is None:
            return
        w, h = event.width, event.height
        if w < 3 or h < 3:
            return  # empty fig

        # resize the figure (in inches)
        dpi = self.figure.dpi.get()
        self.figure.set_figsize_inches(w / dpi, h / dpi)
        self._need_redraw = True

        return False  # finish event propagation?

    def draw(self):
        # Note: FigureCanvasBase.draw() is inconveniently named as it clashes
        # with the deprecated gtk.Widget.draw()

        self._need_redraw = True
        if GTK_WIDGET_DRAWABLE(self):
            self.queue_draw()
            # do a synchronous draw (its less efficient than an async draw,
            # but is required if/when animation is used)
            self.window.process_updates(False)

            ## synchronous draw (needed for animation)
            #x, y, w, h = self.allocation
            #if w<3 or h<3: return # empty fig
            #self._pixmap_prepare (w, h)
            #self._render_figure(self._pixmap, w, h)
            #self._need_redraw = False
            #self.window.draw_drawable (self.style.fg_gc[self.state],
            #                           self._pixmap, 0, 0, 0, 0, w, h)

    def draw_idle(self):
        def idle_draw(*args):
            self.draw()
            self._idleID = 0
            return False

        if self._idleID == 0:
            self._idleID = gobject.idle_add(idle_draw)

    def _renderer_init(self):
        """Override by GTK backends to select a different renderer
        Renderer should provide the methods:
            set_pixmap ()
            set_width_height ()
        that are used by
            _render_figure() / _pixmap_prepare()
        """
        self._renderer = RendererGDK(self, self.figure.dpi)

    def _pixmap_prepare(self, width, height):
        """
        Make sure _._pixmap is at least width, height,
        create new pixmap if necessary
        """
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()

        create_pixmap = False
        if width > self._pixmap_width:
            # increase the pixmap in 10%+ (rather than 1 pixel) steps
            self._pixmap_width = max(int(self._pixmap_width * 1.1), width)
            create_pixmap = True

        if height > self._pixmap_height:
            self._pixmap_height = max(int(self._pixmap_height * 1.1), height)
            create_pixmap = True

        if create_pixmap:
            self._pixmap = gdk.Pixmap(self.window, self._pixmap_width,
                                      self._pixmap_height)
            self._renderer.set_pixmap(self._pixmap)

    def _render_figure(self, pixmap, width, height):
        """used by GTK and GTKcairo. GTKAgg overrides
        """
        self._renderer.set_width_height(width, height)
        self.figure.draw(self._renderer)

    def expose_event(self, widget, event):
        """Expose_event for all GTK backends. Should not be overridden.
        """
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()

        if GTK_WIDGET_DRAWABLE(self):
            if self._need_redraw:
                x, y, w, h = self.allocation
                self._pixmap_prepare(w, h)
                self._render_figure(self._pixmap, w, h)
                self._need_redraw = False

            x, y, w, h = event.area
            self.window.draw_drawable(self.style.fg_gc[self.state],
                                      self._pixmap, x, y, x, y, w, h)
        return False  # finish event propagation?

    def print_figure(self,
                     filename,
                     dpi=150,
                     facecolor='w',
                     edgecolor='w',
                     orientation='portrait'):
        # TODO - use gdk/cairo/agg print_figure?
        root, ext = os.path.splitext(filename)
        ext = ext[1:]
        if ext == '':
            ext = IMAGE_FORMAT_DEFAULT
            filename = filename + '.' + ext

        # save figure settings
        origDPI = self.figure.dpi.get()
        origfacecolor = self.figure.get_facecolor()
        origedgecolor = self.figure.get_edgecolor()
        origWIn, origHIn = self.figure.get_size_inches()

        if self.flags() & gtk.REALIZED == 0:
            # for self.window(for pixmap) and has a side effect of altering
            # figure width,height (via configure-event?)
            gtk.DrawingArea.realize(self)

        self.figure.dpi.set(dpi)
        self.figure.set_facecolor(facecolor)
        self.figure.set_edgecolor(edgecolor)

        ext = ext.lower()
        if ext in ('jpg', 'png'):  # native printing
            width, height = self.get_width_height()
            pixmap = gdk.Pixmap(self.window, width, height)
            self._renderer.set_pixmap(pixmap)
            self._render_figure(pixmap, width, height)

            # jpg colors don't match the display very well, png colors match
            # better
            pixbuf = gdk.Pixbuf(gdk.COLORSPACE_RGB, 0, 8, width, height)
            pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0,
                                     width, height)

            # pixbuf.save() recognises 'jpeg' not 'jpg'
            if ext == 'jpg': ext = 'jpeg'
            try:
                pixbuf.save(filename, ext)
            except gobject.GError, exc:
                error_msg_gtk('Save figure failure:\n%s' % (exc, ),
                              parent=self)

        elif ext in (
                'eps',
                'ps',
                'svg',
        ):
            if ext == 'svg':
                from backend_svg import FigureCanvasSVG as FigureCanvas
            else:
                from backend_ps import FigureCanvasPS as FigureCanvas

            try:
                fc = self.switch_backends(FigureCanvas)
                fc.print_figure(filename, dpi, facecolor, edgecolor,
                                orientation)
            except IOError, exc:
                error_msg_gtk("Save figure failure:\n%s: %s" %
                              (exc.filename, exc.strerror),
                              parent=self)
class FigureCanvasGTK (gtk.DrawingArea, FigureCanvasBase):
    keyvald = {65507 : 'control',
               65505 : 'shift',
               65513 : 'alt',
               65508 : 'control',
               65506 : 'shift',
               65514 : 'alt',
               65361 : 'left',
               65362 : 'up',
               65363 : 'right',
               65364 : 'down',
               65307 : 'escape',
               65470 : 'f1',
               65471 : 'f2',
               65472 : 'f3',
               65473 : 'f4',
               65474 : 'f5',
               65475 : 'f6',
               65476 : 'f7',
               65477 : 'f8',
               65478 : 'f9',
               65479 : 'f10',
               65480 : 'f11',
               65481 : 'f12',
               65300 : 'scroll_lock',
               65299 : 'break',
               65288 : 'backspace',
               65293 : 'enter',
               65379 : 'insert',
               65535 : 'delete',
               65360 : 'home',
               65367 : 'end',
               65365 : 'pageup',
               65366 : 'pagedown',
               65438 : '0',
               65436 : '1',
               65433 : '2',
               65435 : '3',
               65430 : '4',
               65437 : '5',
               65432 : '6',
               65429 : '7',
               65431 : '8',
               65434 : '9',
               65451 : '+',
               65453 : '-',
               65450 : '*',
               65455 : '/',
               65439 : 'dec',
               65421 : 'enter',
               65511 : 'super',
               65512 : 'super',
               65406 : 'alt',
               65289 : 'tab',
               }

    # Setting this as a static constant prevents
    # this resulting expression from leaking
    event_mask = (gdk.BUTTON_PRESS_MASK   |
                  gdk.BUTTON_RELEASE_MASK |
                  gdk.EXPOSURE_MASK       |
                  gdk.KEY_PRESS_MASK      |
                  gdk.KEY_RELEASE_MASK    |
                  gdk.ENTER_NOTIFY_MASK   |
                  gdk.LEAVE_NOTIFY_MASK   |
                  gdk.POINTER_MOTION_MASK |
                  gdk.POINTER_MOTION_HINT_MASK)

    def __init__(self, figure):
        if _debug: print('FigureCanvasGTK.%s' % fn_name())
        FigureCanvasBase.__init__(self, figure)
        gtk.DrawingArea.__init__(self)

        self._idle_draw_id  = 0
        self._need_redraw   = True
        self._pixmap_width  = -1
        self._pixmap_height = -1
        self._lastCursor    = None

        self.connect('scroll_event',         self.scroll_event)
        self.connect('button_press_event',   self.button_press_event)
        self.connect('button_release_event', self.button_release_event)
        self.connect('configure_event',      self.configure_event)
        self.connect('expose_event',         self.expose_event)
        self.connect('key_press_event',      self.key_press_event)
        self.connect('key_release_event',    self.key_release_event)
        self.connect('motion_notify_event',  self.motion_notify_event)
        self.connect('leave_notify_event',   self.leave_notify_event)
        self.connect('enter_notify_event',   self.enter_notify_event)

        self.set_events(self.__class__.event_mask)

        self.set_double_buffered(False)
        self.set_flags(gtk.CAN_FOCUS)
        self._renderer_init()

        self._idle_event_id = gobject.idle_add(self.idle_event)

        self.last_downclick = {}

    def destroy(self):
        #gtk.DrawingArea.destroy(self)
        self.close_event()
        gobject.source_remove(self._idle_event_id)
        if self._idle_draw_id != 0:
            gobject.source_remove(self._idle_draw_id)

    def scroll_event(self, widget, event):
        if _debug: print('FigureCanvasGTK.%s' % fn_name())
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        if event.direction==gdk.SCROLL_UP:
            step = 1
        else:
            step = -1
        FigureCanvasBase.scroll_event(self, x, y, step, guiEvent=event)
        return False  # finish event propagation?

    def button_press_event(self, widget, event):
        if _debug: print('FigureCanvasGTK.%s' % fn_name())
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        dblclick = (event.type == gdk._2BUTTON_PRESS)
        if not dblclick:
            # GTK is the only backend that generates a DOWN-UP-DOWN-DBLCLICK-UP  event
            # sequence for a double click.  All other backends have a DOWN-UP-DBLCLICK-UP
            # sequence.  In order to provide consistency to matplotlib users, we will
            # eat the extra DOWN event in the case that we detect it is part of a double
            # click.
            # first, get the double click time in milliseconds.
            current_time  = event.get_time()
            last_time     = self.last_downclick.get(event.button,0)
            dblclick_time = gtk.settings_get_for_screen(gdk.screen_get_default()).get_property('gtk-double-click-time')
            delta_time    = current_time-last_time
            if delta_time < dblclick_time:
                del self.last_downclick[event.button] # we do not want to eat more than one event.
                return False                          # eat.
            self.last_downclick[event.button] = current_time
        FigureCanvasBase.button_press_event(self, x, y, event.button, dblclick=dblclick, guiEvent=event)
        return False  # finish event propagation?

    def button_release_event(self, widget, event):
        if _debug: print('FigureCanvasGTK.%s' % fn_name())
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        FigureCanvasBase.button_release_event(self, x, y, event.button, guiEvent=event)
        return False  # finish event propagation?

    def key_press_event(self, widget, event):
        if _debug: print('FigureCanvasGTK.%s' % fn_name())
        key = self._get_key(event)
        if _debug: print("hit", key)
        FigureCanvasBase.key_press_event(self, key, guiEvent=event)
        return False  # finish event propagation?

    def key_release_event(self, widget, event):
        if _debug: print('FigureCanvasGTK.%s' % fn_name())
        key = self._get_key(event)
        if _debug: print("release", key)
        FigureCanvasBase.key_release_event(self, key, guiEvent=event)
        return False  # finish event propagation?

    def motion_notify_event(self, widget, event):
        if _debug: print('FigureCanvasGTK.%s' % fn_name())
        if event.is_hint:
            x, y, state = event.window.get_pointer()
        else:
            x, y, state = event.x, event.y, event.state

        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - y
        FigureCanvasBase.motion_notify_event(self, x, y, guiEvent=event)
        return False  # finish event propagation?

    def leave_notify_event(self, widget, event):
        FigureCanvasBase.leave_notify_event(self, event)

    def enter_notify_event(self, widget, event):
        x, y, state = event.window.get_pointer()
        FigureCanvasBase.enter_notify_event(self, event, xy=(x,y))

    def _get_key(self, event):
        if event.keyval in self.keyvald:
            key = self.keyvald[event.keyval]
        elif event.keyval < 256:
            key = chr(event.keyval)
        else:
            key = None

        for key_mask, prefix in (
                                 [gdk.MOD4_MASK, 'super'],
                                 [gdk.MOD1_MASK, 'alt'],
                                 [gdk.CONTROL_MASK, 'ctrl'],):
            if event.state & key_mask:
                key = '{0}+{1}'.format(prefix, key)

        return key

    def configure_event(self, widget, event):
        if _debug: print('FigureCanvasGTK.%s' % fn_name())
        if widget.window is None:
            return
        w, h = event.width, event.height
        if w < 3 or h < 3:
            return # empty fig

        # resize the figure (in inches)
        dpi = self.figure.dpi
        self.figure.set_size_inches (w/dpi, h/dpi)
        self._need_redraw = True

        return False  # finish event propagation?


    def draw(self):
        # Note: FigureCanvasBase.draw() is inconveniently named as it clashes
        # with the deprecated gtk.Widget.draw()

        self._need_redraw = True
        if GTK_WIDGET_DRAWABLE(self):
            self.queue_draw()
            # do a synchronous draw (its less efficient than an async draw,
            # but is required if/when animation is used)
            self.window.process_updates (False)

    def draw_idle(self):
        def idle_draw(*args):
            self.draw()
            self._idle_draw_id = 0
            return False
        if self._idle_draw_id == 0:
            self._idle_draw_id = gobject.idle_add(idle_draw)


    def _renderer_init(self):
        """Override by GTK backends to select a different renderer
        Renderer should provide the methods:
            set_pixmap ()
            set_width_height ()
        that are used by
            _render_figure() / _pixmap_prepare()
        """
        self._renderer = RendererGDK (self, self.figure.dpi)


    def _pixmap_prepare(self, width, height):
        """
        Make sure _._pixmap is at least width, height,
        create new pixmap if necessary
        """
        if _debug: print('FigureCanvasGTK.%s' % fn_name())

        create_pixmap = False
        if width > self._pixmap_width:
            # increase the pixmap in 10%+ (rather than 1 pixel) steps
            self._pixmap_width  = max (int (self._pixmap_width  * 1.1),
                                       width)
            create_pixmap = True

        if height > self._pixmap_height:
            self._pixmap_height = max (int (self._pixmap_height * 1.1),
                                           height)
            create_pixmap = True

        if create_pixmap:
            self._pixmap = gdk.Pixmap (self.window, self._pixmap_width,
                                       self._pixmap_height)
            self._renderer.set_pixmap (self._pixmap)


    def _render_figure(self, pixmap, width, height):
        """used by GTK and GTKcairo. GTKAgg overrides
        """
        self._renderer.set_width_height (width, height)
        self.figure.draw (self._renderer)


    def expose_event(self, widget, event):
        """Expose_event for all GTK backends. Should not be overridden.
        """
        if _debug: print('FigureCanvasGTK.%s' % fn_name())

        if GTK_WIDGET_DRAWABLE(self):
            if self._need_redraw:
                x, y, w, h = self.allocation
                self._pixmap_prepare (w, h)
                self._render_figure(self._pixmap, w, h)
                self._need_redraw = False

            x, y, w, h = event.area
            self.window.draw_drawable (self.style.fg_gc[self.state],
                                       self._pixmap, x, y, x, y, w, h)
        return False  # finish event propagation?

    filetypes = FigureCanvasBase.filetypes.copy()
    filetypes['jpg'] = 'JPEG'
    filetypes['jpeg'] = 'JPEG'
    filetypes['png'] = 'Portable Network Graphics'

    def print_jpeg(self, filename, *args, **kwargs):
        return self._print_image(filename, 'jpeg')
    print_jpg = print_jpeg

    def print_png(self, filename, *args, **kwargs):
        return self._print_image(filename, 'png')

    def _print_image(self, filename, format):
        if self.flags() & gtk.REALIZED == 0:
            # for self.window(for pixmap) and has a side effect of altering
            # figure width,height (via configure-event?)
            gtk.DrawingArea.realize(self)

        width, height = self.get_width_height()
        pixmap = gdk.Pixmap (self.window, width, height)
        self._renderer.set_pixmap (pixmap)
        self._render_figure(pixmap, width, height)

        # jpg colors don't match the display very well, png colors match
        # better
        pixbuf = gdk.Pixbuf(gdk.COLORSPACE_RGB, 0, 8, width, height)
        pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(),
                                     0, 0, 0, 0, width, height)

        if is_string_like(filename):
            try:
                pixbuf.save(filename, format)
            except gobject.GError as exc:
                error_msg_gtk('Save figure failure:\n%s' % (exc,), parent=self)
        elif is_writable_file_like(filename):
            if hasattr(pixbuf, 'save_to_callback'):
                def save_callback(buf, data=None):
                    data.write(buf)
                try:
                    pixbuf.save_to_callback(save_callback, format, user_data=filename)
                except gobject.GError as exc:
                    error_msg_gtk('Save figure failure:\n%s' % (exc,), parent=self)
            else:
                raise ValueError("Saving to a Python file-like object is only supported by PyGTK >= 2.8")
        else:
            raise ValueError("filename must be a path or a file-like object")

    def new_timer(self, *args, **kwargs):
        """
        Creates a new backend-specific subclass of :class:`backend_bases.Timer`.
        This is useful for getting periodic events through the backend's native
        event loop. Implemented only for backends with GUIs.

        optional arguments:

        *interval*
          Timer interval in milliseconds
        *callbacks*
          Sequence of (func, args, kwargs) where func(*args, **kwargs) will
          be executed by the timer every *interval*.
        """
        return TimerGTK(*args, **kwargs)

    def flush_events(self):
        gtk.gdk.threads_enter()
        while gtk.events_pending():
            gtk.main_iteration(True)
        gtk.gdk.flush()
        gtk.gdk.threads_leave()

    def start_event_loop(self,timeout):
        FigureCanvasBase.start_event_loop_default(self,timeout)
    start_event_loop.__doc__=FigureCanvasBase.start_event_loop_default.__doc__

    def stop_event_loop(self):
        FigureCanvasBase.stop_event_loop_default(self)
    stop_event_loop.__doc__=FigureCanvasBase.stop_event_loop_default.__doc__
Пример #8
0
class FigureCanvasGTK(gtk.DrawingArea, FigureCanvasBase):
    keyvald = {
        65507: "control",
        65505: "shift",
        65513: "alt",
        65508: "control",
        65506: "shift",
        65514: "alt",
        65361: "left",
        65362: "up",
        65363: "right",
        65364: "down",
        65307: "escape",
        65470: "f1",
        65471: "f2",
        65472: "f3",
        65473: "f4",
        65474: "f5",
        65475: "f6",
        65476: "f7",
        65477: "f8",
        65478: "f9",
        65479: "f10",
        65480: "f11",
        65481: "f12",
        65300: "scroll_lock",
        65299: "break",
        65288: "backspace",
        65293: "enter",
        65379: "insert",
        65535: "delete",
        65360: "home",
        65367: "end",
        65365: "pageup",
        65366: "pagedown",
        65438: "0",
        65436: "1",
        65433: "2",
        65435: "3",
        65430: "4",
        65437: "5",
        65432: "6",
        65429: "7",
        65431: "8",
        65434: "9",
        65451: "+",
        65453: "-",
        65450: "*",
        65455: "/",
        65439: "dec",
        65421: "enter",
    }

    # Setting this as a static constant prevents
    # this resulting expression from leaking
    event_mask = (
        gdk.BUTTON_PRESS_MASK
        | gdk.BUTTON_RELEASE_MASK
        | gdk.EXPOSURE_MASK
        | gdk.KEY_PRESS_MASK
        | gdk.KEY_RELEASE_MASK
        | gdk.LEAVE_NOTIFY_MASK
        | gdk.POINTER_MOTION_MASK
        | gdk.POINTER_MOTION_HINT_MASK
    )

    def __init__(self, figure):
        if _debug:
            print "FigureCanvasGTK.%s" % fn_name()
        FigureCanvasBase.__init__(self, figure)
        gtk.DrawingArea.__init__(self)

        self._idleID = 0
        self._need_redraw = True
        self._pixmap_width = -1
        self._pixmap_height = -1
        self._lastCursor = None

        self.connect("scroll_event", self.scroll_event)
        self.connect("button_press_event", self.button_press_event)
        self.connect("button_release_event", self.button_release_event)
        self.connect("configure_event", self.configure_event)
        self.connect("expose_event", self.expose_event)
        self.connect("key_press_event", self.key_press_event)
        self.connect("key_release_event", self.key_release_event)
        self.connect("motion_notify_event", self.motion_notify_event)

        self.set_events(self.__class__.event_mask)

        self.set_double_buffered(False)
        self.set_flags(gtk.CAN_FOCUS)
        self._renderer_init()

    def scroll_event(self, widget, event):
        if _debug:
            print "FigureCanvasGTK.%s" % fn_name()
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        if event.direction == gdk.SCROLL_UP:
            direction = "up"
        else:
            direction = "down"
        FigureCanvasBase.scroll_event(self, x, y, direction)
        return False  # finish event propagation?

    def button_press_event(self, widget, event):
        if _debug:
            print "FigureCanvasGTK.%s" % fn_name()
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        FigureCanvasBase.button_press_event(self, x, y, event.button)
        return False  # finish event propagation?

    def button_release_event(self, widget, event):
        if _debug:
            print "FigureCanvasGTK.%s" % fn_name()
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        FigureCanvasBase.button_release_event(self, x, y, event.button)
        return False  # finish event propagation?

    def key_press_event(self, widget, event):
        if _debug:
            print "FigureCanvasGTK.%s" % fn_name()
        key = self._get_key(event)
        if _debug:
            print "hit", key
        FigureCanvasBase.key_press_event(self, key)
        return False  # finish event propagation?

    def key_release_event(self, widget, event):
        if _debug:
            print "FigureCanvasGTK.%s" % fn_name()
        key = self._get_key(event)
        if _debug:
            print "release", key
        FigureCanvasBase.key_release_event(self, key)
        return False  # finish event propagation?

    def motion_notify_event(self, widget, event):
        if _debug:
            print "FigureCanvasGTK.%s" % fn_name()
        if event.is_hint:
            x, y, state = event.window.get_pointer()
        else:
            x, y, state = event.x, event.y, event.state

        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - y
        FigureCanvasBase.motion_notify_event(self, x, y)
        return False  # finish event propagation?

    def _get_key(self, event):
        if self.keyvald.has_key(event.keyval):
            key = self.keyvald[event.keyval]
        elif event.keyval < 256:
            key = chr(event.keyval)
        else:
            key = None

        ctrl = event.state & gdk.CONTROL_MASK
        shift = event.state & gdk.SHIFT_MASK
        return key

    def configure_event(self, widget, event):
        if _debug:
            print "FigureCanvasGTK.%s" % fn_name()
        if widget.window is None:
            return
        w, h = event.width, event.height
        if w < 3 or h < 3:
            return  # empty fig

        # resize the figure (in inches)
        dpi = self.figure.dpi
        self.figure.set_size_inches(w / dpi, h / dpi)
        self._need_redraw = True

        return False  # finish event propagation?

    def draw(self):
        # Note: FigureCanvasBase.draw() is inconveniently named as it clashes
        # with the deprecated gtk.Widget.draw()

        self._need_redraw = True
        if GTK_WIDGET_DRAWABLE(self):
            self.queue_draw()
            # do a synchronous draw (its less efficient than an async draw,
            # but is required if/when animation is used)
            self.window.process_updates(False)

    def draw_idle(self):
        def idle_draw(*args):
            self.draw()
            self._idleID = 0
            return False

        if self._idleID == 0:
            self._idleID = gobject.idle_add(idle_draw)

    def _renderer_init(self):
        """Override by GTK backends to select a different renderer
        Renderer should provide the methods:
            set_pixmap ()
            set_width_height ()
        that are used by
            _render_figure() / _pixmap_prepare()
        """
        self._renderer = RendererGDK(self, self.figure.dpi)

    def _pixmap_prepare(self, width, height):
        """
        Make sure _._pixmap is at least width, height,
        create new pixmap if necessary
        """
        if _debug:
            print "FigureCanvasGTK.%s" % fn_name()

        create_pixmap = False
        if width > self._pixmap_width:
            # increase the pixmap in 10%+ (rather than 1 pixel) steps
            self._pixmap_width = max(int(self._pixmap_width * 1.1), width)
            create_pixmap = True

        if height > self._pixmap_height:
            self._pixmap_height = max(int(self._pixmap_height * 1.1), height)
            create_pixmap = True

        if create_pixmap:
            self._pixmap = gdk.Pixmap(self.window, self._pixmap_width, self._pixmap_height)
            self._renderer.set_pixmap(self._pixmap)

    def _render_figure(self, pixmap, width, height):
        """used by GTK and GTKcairo. GTKAgg overrides
        """
        self._renderer.set_width_height(width, height)
        self.figure.draw(self._renderer)

    def expose_event(self, widget, event):
        """Expose_event for all GTK backends. Should not be overridden.
        """
        if _debug:
            print "FigureCanvasGTK.%s" % fn_name()

        if GTK_WIDGET_DRAWABLE(self):
            if self._need_redraw:
                x, y, w, h = self.allocation
                self._pixmap_prepare(w, h)
                self._render_figure(self._pixmap, w, h)
                self._need_redraw = False

            x, y, w, h = event.area
            self.window.draw_drawable(self.style.fg_gc[self.state], self._pixmap, x, y, x, y, w, h)
        return False  # finish event propagation?

    filetypes = FigureCanvasBase.filetypes.copy()
    filetypes["jpg"] = "JPEG"
    filetypes["jpeg"] = "JPEG"
    filetypes["png"] = "Portable Network Graphics"

    def print_jpeg(self, filename, *args, **kwargs):
        return self._print_image(filename, "jpeg")

    print_jpg = print_jpeg

    def print_png(self, filename, *args, **kwargs):
        return self._print_image(filename, "png")

    def _print_image(self, filename, format):
        if self.flags() & gtk.REALIZED == 0:
            # for self.window(for pixmap) and has a side effect of altering
            # figure width,height (via configure-event?)
            gtk.DrawingArea.realize(self)

        width, height = self.get_width_height()
        pixmap = gdk.Pixmap(self.window, width, height)
        self._renderer.set_pixmap(pixmap)
        self._render_figure(pixmap, width, height)

        # jpg colors don't match the display very well, png colors match
        # better
        pixbuf = gdk.Pixbuf(gdk.COLORSPACE_RGB, 0, 8, width, height)
        pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0, width, height)

        if is_string_like(filename):
            try:
                pixbuf.save(filename, format)
            except gobject.GError, exc:
                error_msg_gtk("Save figure failure:\n%s" % (exc,), parent=self)
        elif is_writable_file_like(filename):
            if hasattr(pixbuf, "save_to_callback"):

                def save_callback(buf, data=None):
                    data.write(buf)

                try:
                    pixbuf.save_to_callback(save_callback, format, user_data=filename)
                except gobject.GError, exc:
                    error_msg_gtk("Save figure failure:\n%s" % (exc,), parent=self)
Пример #9
0
class FigureCanvasGTK (gtk.DrawingArea, FigureCanvasBase):
    keyvald = {65507 : 'control',
               65505 : 'shift',
               65513 : 'alt',
               65508 : 'control',
               65506 : 'shift',
               65514 : 'alt',
               65361 : 'left',
               65362 : 'up',
               65363 : 'right',
               65364 : 'down',
               }

    def __init__(self, figure):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        FigureCanvasBase.__init__(self, figure)
        gtk.DrawingArea.__init__(self)

        self._idleID        = 0
        self._need_redraw   = True
        self._pixmap_width  = -1
        self._pixmap_height = -1
        self._lastCursor    = None

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

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

        self.set_double_buffered(False)
        self.set_flags(gtk.CAN_FOCUS)
        self._renderer_init()


    #def resize(self, w, h):
    #    'set the drawing area size in pixels'
    #    winw, winh = self.parent.parent.get_size()
    #    tmp, tmp, myw, myh = self.allocation
    #    padw = winw-myw
    #    padh = winh-myh
    #    self.parent.parent.resize(w+padw, h+padh)

    def button_press_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        FigureCanvasBase.button_press_event(self, x, y, event.button)
        return False  # finish event propagation?

    def button_release_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        FigureCanvasBase.button_release_event(self, x, y, event.button)
        return False  # finish event propagation?

    def key_press_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        key = self._get_key(event)
        if _debug: print "hit", key
        FigureCanvasBase.key_press_event(self, key)
        return False  # finish event propagation?

    def key_release_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        key = self._get_key(event)
        if _debug: print "release", key
        FigureCanvasBase.key_release_event(self, key)
        return False  # finish event propagation?

    def motion_notify_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        if event.is_hint:
            x, y, state = event.window.get_pointer()
        else:
            x, y, state = event.x, event.y, event.state

        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - y
        FigureCanvasBase.motion_notify_event(self, x, y)
        return False  # finish event propagation?

    def _get_key(self, event):
        if self.keyvald.has_key(event.keyval):
            key = self.keyvald[event.keyval]
        elif event.keyval <256:
            key = chr(event.keyval)
        else:
            key = None

        ctrl  = event.state & gdk.CONTROL_MASK
        shift = event.state & gdk.SHIFT_MASK
        return key


    def configure_event(self, widget, event):
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()
        if widget.window is None:
            return
        w, h = event.width, event.height
        if w<3 or h<3:
            return # empty fig

        # resize the figure (in inches)
        dpi = self.figure.dpi.get()
        self.figure.set_figsize_inches (w/dpi, h/dpi)
        self._need_redraw = True

        #self.resize_event()
        return False  # finish event propagation?


    def draw(self):
        # synchronous window redraw (like GTK+ 1.2 used to do)
        # Note: this does not follow the usual way that GTK redraws,
        # which is asynchronous redraw using calls to gtk_widget_queue_draw(),
        # which triggers an expose-event

        # GTK+ 2.x style draw()
        #self._need_redraw = True
        #self.queue_draw()

        # synchronous draw (needed for animation)
        x, y, w, h = self.allocation
        if w<3 or h<3: return # empty fig

        self._pixmap_prepare (w, h)
        self._render_figure(self._pixmap, w, h)
        self._need_redraw = False
        self.window.draw_drawable (self.style.fg_gc[self.state],
                                   self._pixmap, 0, 0, 0, 0, w, h)


    def draw_idle(self):
        def idle_draw(*args):
            self.draw()
            self._idleID = 0
            return False
        if self._idleID==0:
            self._idleID = gobject.idle_add(idle_draw)


    def _renderer_init(self):
        """Override by GTK backends to select a different renderer
        Renderer should provide the methods:
            set_pixmap ()
            set_width_height ()
        that are used by
            _render_figure() / _pixmap_prepare()
        """
        self._renderer = RendererGDK (self, self.figure.dpi)


    def _pixmap_prepare(self, width, height):
        """
        Make sure _._pixmap is at least width, height,
        create new pixmap if necessary
        """
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()

        create_pixmap = False
        if width > self._pixmap_width:
            # increase the pixmap in 10%+ (rather than 1 pixel) steps
            self._pixmap_width  = max (int (self._pixmap_width  * 1.1),
                                       width)
            create_pixmap = True

        if height > self._pixmap_height:
            self._pixmap_height = max (int (self._pixmap_height * 1.1),
                                           height)
            create_pixmap = True

        if create_pixmap:
            self._pixmap = gdk.Pixmap (self.window, self._pixmap_width,
                                       self._pixmap_height)
            self._renderer.set_pixmap (self._pixmap)


    def _render_figure(self, pixmap, width, height):
        """used by GTK and GTKcairo. GTKAgg overrides
        """
        self._renderer.set_width_height (width, height)
        self.figure.draw (self._renderer)


    def expose_event(self, widget, event):
        """Expose_event for all GTK backends. Should not be overridden.
        """
        if _debug: print 'FigureCanvasGTK.%s' % fn_name()

        if GTK_WIDGET_DRAWABLE(self):
            if self._need_redraw:
                x, y, w, h = self.allocation
                self._pixmap_prepare (w, h)
                self._render_figure(self._pixmap, w, h)
                self._need_redraw = False

            x, y, w, h = event.area
            self.window.draw_drawable (self.style.fg_gc[self.state],
                                       self._pixmap, x, y, x, y, w, h)
        return False  # finish event propagation?


    def print_figure(self, filename, dpi=150, facecolor='w', edgecolor='w',
                     orientation='portrait'):
        # TODO - use gdk/cairo/agg print_figure?
        root, ext = os.path.splitext(filename)
        ext = ext[1:]
        if ext == '':
            ext      = IMAGE_FORMAT_DEFAULT
            filename = filename + '.' + ext

        # save figure settings
        origDPI       = self.figure.dpi.get()
        origfacecolor = self.figure.get_facecolor()
        origedgecolor = self.figure.get_edgecolor()
        origWIn, origHIn = self.figure.get_size_inches()

        if self.flags() & gtk.REALIZED == 0:
            # for self.window(for pixmap) and has a side effect of altering
            # figure width,height (via configure-event?)
            gtk.DrawingArea.realize(self)

        self.figure.dpi.set(dpi)
        self.figure.set_facecolor(facecolor)
        self.figure.set_edgecolor(edgecolor)

        ext = ext.lower()
        if ext in ('jpg', 'png'):          # native printing
            width, height = self.get_width_height()
            pixmap = gdk.Pixmap (self.window, width, height)
            self._renderer.set_pixmap (pixmap)
            self._render_figure(pixmap, width, height)

            # jpg colors don't match the display very well, png colors match
            # better
            pixbuf = gdk.Pixbuf(gdk.COLORSPACE_RGB, 0, 8, width, height)
            pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(),
                                     0, 0, 0, 0, width, height)

            # pixbuf.save() recognises 'jpeg' not 'jpg'
            if ext == 'jpg': ext = 'jpeg'
            try:
                pixbuf.save(filename, ext)
            except gobject.GError, exc:
                error_msg_gtk('Save figure failure:\n%s' % (exc,), parent=self)

        elif ext in ('eps', 'ps', 'svg',):
            if ext == 'svg':
                from backend_svg import FigureCanvasSVG as FigureCanvas
            else:
                from backend_ps  import FigureCanvasPS  as FigureCanvas

            try:
                fc = self.switch_backends(FigureCanvas)
                fc.print_figure(filename, dpi, facecolor, edgecolor,
                                orientation)
            except IOError, exc:
                error_msg_gtk("Save figure failure:\n%s: %s" %
                          (exc.filename, exc.strerror), parent=self)
Пример #10
0
class FigureCanvasGTK(gtk.DrawingArea, FigureCanvasBase):
    keyvald = {
        65507: "control",
        65505: "shift",
        65513: "alt",
        65508: "control",
        65506: "shift",
        65514: "alt",
        65361: "left",
        65362: "up",
        65363: "right",
        65364: "down",
        65307: "escape",
        65470: "f1",
        65471: "f2",
        65472: "f3",
        65473: "f4",
        65474: "f5",
        65475: "f6",
        65476: "f7",
        65477: "f8",
        65478: "f9",
        65479: "f10",
        65480: "f11",
        65481: "f12",
        65300: "scroll_lock",
        65299: "break",
        65288: "backspace",
        65293: "enter",
        65379: "insert",
        65535: "delete",
        65360: "home",
        65367: "end",
        65365: "pageup",
        65366: "pagedown",
        65438: "0",
        65436: "1",
        65433: "2",
        65435: "3",
        65430: "4",
        65437: "5",
        65432: "6",
        65429: "7",
        65431: "8",
        65434: "9",
        65451: "+",
        65453: "-",
        65450: "*",
        65455: "/",
        65439: "dec",
        65421: "enter",
        65511: "super",
        65512: "super",
        65406: "alt",
        65289: "tab",
    }

    # Setting this as a static constant prevents
    # this resulting expression from leaking
    event_mask = (
        gdk.BUTTON_PRESS_MASK
        | gdk.BUTTON_RELEASE_MASK
        | gdk.EXPOSURE_MASK
        | gdk.KEY_PRESS_MASK
        | gdk.KEY_RELEASE_MASK
        | gdk.ENTER_NOTIFY_MASK
        | gdk.LEAVE_NOTIFY_MASK
        | gdk.POINTER_MOTION_MASK
        | gdk.POINTER_MOTION_HINT_MASK
    )

    def __init__(self, figure):
        if self.__class__ == matplotlib.backends.backend_gtk.FigureCanvasGTK:
            warn_deprecated(
                "2.0",
                message="The GTK backend is "
                "deprecated. It is untested, known to be "
                "broken and will be removed in Matplotlib 2.2. "
                "Use the GTKAgg backend instead. "
                "See Matplotlib usage FAQ for"
                " more info on backends.",
                alternative="GTKAgg",
            )
        if _debug:
            print("FigureCanvasGTK.%s" % fn_name())
        FigureCanvasBase.__init__(self, figure)
        gtk.DrawingArea.__init__(self)

        self._idle_draw_id = 0
        self._need_redraw = True
        self._pixmap_width = -1
        self._pixmap_height = -1
        self._lastCursor = None

        self.connect("scroll_event", self.scroll_event)
        self.connect("button_press_event", self.button_press_event)
        self.connect("button_release_event", self.button_release_event)
        self.connect("configure_event", self.configure_event)
        self.connect("expose_event", self.expose_event)
        self.connect("key_press_event", self.key_press_event)
        self.connect("key_release_event", self.key_release_event)
        self.connect("motion_notify_event", self.motion_notify_event)
        self.connect("leave_notify_event", self.leave_notify_event)
        self.connect("enter_notify_event", self.enter_notify_event)

        self.set_events(self.__class__.event_mask)

        self.set_double_buffered(False)
        self.set_flags(gtk.CAN_FOCUS)
        self._renderer_init()

        self.last_downclick = {}

    def destroy(self):
        # gtk.DrawingArea.destroy(self)
        self.close_event()
        if self._idle_draw_id != 0:
            gobject.source_remove(self._idle_draw_id)

    def scroll_event(self, widget, event):
        if _debug:
            print("FigureCanvasGTK.%s" % fn_name())
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        if event.direction == gdk.SCROLL_UP:
            step = 1
        else:
            step = -1
        FigureCanvasBase.scroll_event(self, x, y, step, guiEvent=event)
        return False  # finish event propagation?

    def button_press_event(self, widget, event):
        if _debug:
            print("FigureCanvasGTK.%s" % fn_name())
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        dblclick = event.type == gdk._2BUTTON_PRESS
        if not dblclick:
            # GTK is the only backend that generates a DOWN-UP-DOWN-DBLCLICK-UP  event
            # sequence for a double click.  All other backends have a DOWN-UP-DBLCLICK-UP
            # sequence.  In order to provide consistency to matplotlib users, we will
            # eat the extra DOWN event in the case that we detect it is part of a double
            # click.
            # first, get the double click time in milliseconds.
            current_time = event.get_time()
            last_time = self.last_downclick.get(event.button, 0)
            dblclick_time = gtk.settings_get_for_screen(gdk.screen_get_default()).get_property("gtk-double-click-time")
            delta_time = current_time - last_time
            if delta_time < dblclick_time:
                del self.last_downclick[event.button]  # we do not want to eat more than one event.
                return False  # eat.
            self.last_downclick[event.button] = current_time
        FigureCanvasBase.button_press_event(self, x, y, event.button, dblclick=dblclick, guiEvent=event)
        return False  # finish event propagation?

    def button_release_event(self, widget, event):
        if _debug:
            print("FigureCanvasGTK.%s" % fn_name())
        x = event.x
        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - event.y
        FigureCanvasBase.button_release_event(self, x, y, event.button, guiEvent=event)
        return False  # finish event propagation?

    def key_press_event(self, widget, event):
        if _debug:
            print("FigureCanvasGTK.%s" % fn_name())
        key = self._get_key(event)
        if _debug:
            print("hit", key)
        FigureCanvasBase.key_press_event(self, key, guiEvent=event)
        return True  # stop event propagation

    def key_release_event(self, widget, event):
        if _debug:
            print("FigureCanvasGTK.%s" % fn_name())
        key = self._get_key(event)
        if _debug:
            print("release", key)
        FigureCanvasBase.key_release_event(self, key, guiEvent=event)
        return True  # stop event propagation

    def motion_notify_event(self, widget, event):
        if _debug:
            print("FigureCanvasGTK.%s" % fn_name())
        if event.is_hint:
            x, y, state = event.window.get_pointer()
        else:
            x, y, state = event.x, event.y, event.state

        # flipy so y=0 is bottom of canvas
        y = self.allocation.height - y
        FigureCanvasBase.motion_notify_event(self, x, y, guiEvent=event)
        return False  # finish event propagation?

    def leave_notify_event(self, widget, event):
        FigureCanvasBase.leave_notify_event(self, event)

    def enter_notify_event(self, widget, event):
        x, y, state = event.window.get_pointer()
        FigureCanvasBase.enter_notify_event(self, event, xy=(x, y))

    def _get_key(self, event):
        if event.keyval in self.keyvald:
            key = self.keyvald[event.keyval]
        elif event.keyval < 256:
            key = chr(event.keyval)
        else:
            key = None

        for key_mask, prefix in ([gdk.MOD4_MASK, "super"], [gdk.MOD1_MASK, "alt"], [gdk.CONTROL_MASK, "ctrl"]):
            if event.state & key_mask:
                key = "{0}+{1}".format(prefix, key)

        return key

    def configure_event(self, widget, event):
        if _debug:
            print("FigureCanvasGTK.%s" % fn_name())
        if widget.window is None:
            return
        w, h = event.width, event.height
        if w < 3 or h < 3:
            return  # empty fig

        # resize the figure (in inches)
        dpi = self.figure.dpi
        self.figure.set_size_inches(w / dpi, h / dpi, forward=False)
        self._need_redraw = True

        return False  # finish event propagation?

    def draw(self):
        # Note: FigureCanvasBase.draw() is inconveniently named as it clashes
        # with the deprecated gtk.Widget.draw()

        self._need_redraw = True
        if GTK_WIDGET_DRAWABLE(self):
            self.queue_draw()
            # do a synchronous draw (its less efficient than an async draw,
            # but is required if/when animation is used)
            self.window.process_updates(False)

    def draw_idle(self):
        if self._idle_draw_id != 0:
            return

        def idle_draw(*args):
            try:
                self.draw()
            finally:
                self._idle_draw_id = 0
            return False

        self._idle_draw_id = gobject.idle_add(idle_draw)

    def _renderer_init(self):
        """Override by GTK backends to select a different renderer
        Renderer should provide the methods:
            set_pixmap ()
            set_width_height ()
        that are used by
            _render_figure() / _pixmap_prepare()
        """
        self._renderer = RendererGDK(self, self.figure.dpi)

    def _pixmap_prepare(self, width, height):
        """
        Make sure _._pixmap is at least width, height,
        create new pixmap if necessary
        """
        if _debug:
            print("FigureCanvasGTK.%s" % fn_name())

        create_pixmap = False
        if width > self._pixmap_width:
            # increase the pixmap in 10%+ (rather than 1 pixel) steps
            self._pixmap_width = max(int(self._pixmap_width * 1.1), width)
            create_pixmap = True

        if height > self._pixmap_height:
            self._pixmap_height = max(int(self._pixmap_height * 1.1), height)
            create_pixmap = True

        if create_pixmap:
            self._pixmap = gdk.Pixmap(self.window, self._pixmap_width, self._pixmap_height)
            self._renderer.set_pixmap(self._pixmap)

    def _render_figure(self, pixmap, width, height):
        """used by GTK and GTKcairo. GTKAgg overrides
        """
        self._renderer.set_width_height(width, height)
        self.figure.draw(self._renderer)

    def expose_event(self, widget, event):
        """Expose_event for all GTK backends. Should not be overridden.
        """
        if _debug:
            print("FigureCanvasGTK.%s" % fn_name())

        if GTK_WIDGET_DRAWABLE(self):
            if self._need_redraw:
                x, y, w, h = self.allocation
                self._pixmap_prepare(w, h)
                self._render_figure(self._pixmap, w, h)
                self._need_redraw = False

            x, y, w, h = event.area
            self.window.draw_drawable(self.style.fg_gc[self.state], self._pixmap, x, y, x, y, w, h)
        return False  # finish event propagation?

    filetypes = FigureCanvasBase.filetypes.copy()
    filetypes["jpg"] = "JPEG"
    filetypes["jpeg"] = "JPEG"
    filetypes["png"] = "Portable Network Graphics"

    def print_jpeg(self, filename, *args, **kwargs):
        return self._print_image(filename, "jpeg")

    print_jpg = print_jpeg

    def print_png(self, filename, *args, **kwargs):
        return self._print_image(filename, "png")

    def _print_image(self, filename, format, *args, **kwargs):
        if self.flags() & gtk.REALIZED == 0:
            # for self.window(for pixmap) and has a side effect of altering
            # figure width,height (via configure-event?)
            gtk.DrawingArea.realize(self)

        width, height = self.get_width_height()
        pixmap = gdk.Pixmap(self.window, width, height)
        self._renderer.set_pixmap(pixmap)
        self._render_figure(pixmap, width, height)

        # jpg colors don't match the display very well, png colors match
        # better
        pixbuf = gdk.Pixbuf(gdk.COLORSPACE_RGB, 0, 8, width, height)
        pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0, width, height)

        # set the default quality, if we are writing a JPEG.
        # http://www.pygtk.org/docs/pygtk/class-gdkpixbuf.html#method-gdkpixbuf--save
        options = cbook.restrict_dict(kwargs, ["quality"])
        if format in ["jpg", "jpeg"]:
            if "quality" not in options:
                options["quality"] = rcParams["savefig.jpeg_quality"]

            options["quality"] = str(options["quality"])

        if is_string_like(filename):
            try:
                pixbuf.save(filename, format, options=options)
            except gobject.GError as exc:
                error_msg_gtk("Save figure failure:\n%s" % (exc,), parent=self)
        elif is_writable_file_like(filename):
            if hasattr(pixbuf, "save_to_callback"):

                def save_callback(buf, data=None):
                    data.write(buf)

                try:
                    pixbuf.save_to_callback(save_callback, format, user_data=filename, options=options)
                except gobject.GError as exc:
                    error_msg_gtk("Save figure failure:\n%s" % (exc,), parent=self)
            else:
                raise ValueError("Saving to a Python file-like object is only supported by PyGTK >= 2.8")
        else:
            raise ValueError("filename must be a path or a file-like object")

    def new_timer(self, *args, **kwargs):
        """
        Creates a new backend-specific subclass of :class:`backend_bases.Timer`.
        This is useful for getting periodic events through the backend's native
        event loop. Implemented only for backends with GUIs.

        Other Parameters
        ----------------
        interval : scalar
            Timer interval in milliseconds
        callbacks : list
            Sequence of (func, args, kwargs) where ``func(*args, **kwargs)``
            will be executed by the timer every *interval*.
        """
        return TimerGTK(*args, **kwargs)

    def flush_events(self):
        gtk.gdk.threads_enter()
        while gtk.events_pending():
            gtk.main_iteration(True)
        gtk.gdk.flush()
        gtk.gdk.threads_leave()

    def start_event_loop(self, timeout):
        FigureCanvasBase.start_event_loop_default(self, timeout)

    start_event_loop.__doc__ = FigureCanvasBase.start_event_loop_default.__doc__

    def stop_event_loop(self):
        FigureCanvasBase.stop_event_loop_default(self)

    stop_event_loop.__doc__ = FigureCanvasBase.stop_event_loop_default.__doc__