diff --git a/.travis.yml b/.travis.yml
index 9342444f8fca..3ad28f824cb2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -27,6 +27,9 @@ addons:
- graphviz
- libgeos-dev
- otf-freefont
+ - libpangocairo-1.0-0
+ - libgirepository-1.0-1
+ - gir1.2-gtk-3.0
# - fonts-humor-sans
# sources:
# - debian-sid
@@ -112,7 +115,7 @@ install:
pip install --upgrade setuptools
- |
# Install dependencies from pypi
- pip install $PRE python-dateutil $NUMPY pyparsing!=2.1.6 $PANDAS pep8 cycler coveralls coverage
+ pip install $PRE python-dateutil $NUMPY pyparsing!=2.1.6 $PANDAS pep8 cycler coveralls coverage pgi cairocffi
pip install $PRE -r doc-requirements.txt
# Install nose from a build which has partial
@@ -160,7 +163,7 @@ script:
if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
python tests.py $NOSE_ARGS $TEST_ARGS
else
- gdb -return-child-result -batch -ex r -ex bt --args python $PYTHON_ARGS tests.py $NOSE_ARGS $TEST_ARGS
+ xvfb-run gdb -return-child-result -batch -ex r -ex bt --args python $PYTHON_ARGS tests.py $NOSE_ARGS $TEST_ARGS
fi
else
echo The following args are passed to pytest $PYTEST_ARGS
diff --git a/INSTALL b/INSTALL
index aade12aab122..0fabeb3c5ccf 100644
--- a/INSTALL
+++ b/INSTALL
@@ -235,6 +235,10 @@ backends and the capabilities they provide.
:term:`pyqt` 4.4 or later
The Qt4 widgets library python wrappers for the Qt4Agg backend
+:term:`PyGObject` or `pgi`
+ For Gtk3, MPL requires the installation of a GObject introspection library
+ for python, either `PyGObject` (also known as gi) or `pgi`.
+
:term:`pygtk` 2.4 or later
The python wrappers for the GTK widgets library for use with the
GTK or GTKAgg backend
diff --git a/doc/glossary/index.rst b/doc/glossary/index.rst
index 5f0b683f14cf..ac77926b4952 100644
--- a/doc/glossary/index.rst
+++ b/doc/glossary/index.rst
@@ -64,6 +64,17 @@ Glossary
channel. PDF was designed in part as a next-generation document
format to replace postscript
+ pgi
+ `pgi ` exists as a relatively new
+ python wrapper to GTK3 and acts as a pure python alternative to PyGObject.
+ pgi still exists in its infancy, currently missing many features of
+ PyGObject. However matplotlib does not use any of these missing features.
+
+ PyGObject
+ Like :term:`pygtk`, `PyGObject ` provides
+ python wrappers for the :term:`GTK` widgets library; unlike pygtk,
+ PyGObject wraps GTK3 instead of the now obsolete GTK2.
+
pygtk
`pygtk `_ provides python wrappers for
the :term:`GTK` widgets library for use with the GTK or GTKAgg
diff --git a/doc/users/whats_new/2015-07-30_pgi.rst b/doc/users/whats_new/2015-07-30_pgi.rst
new file mode 100644
index 000000000000..08d303d84280
--- /dev/null
+++ b/doc/users/whats_new/2015-07-30_pgi.rst
@@ -0,0 +1,9 @@
+PGI - Pure Python GObject Introspection Bindings
+------------------------------------------------
+
+For the GTK3 backend, matplotlib now supports PGI bindings as an alternative
+to PyGObject. By default matplotlib will still use PyGObject, otherwise it
+will look for pgi. You can change this behaviour through the rcParam
+backend.gi_preference which takes either a string, or a list of strings in
+order of preference.
+
diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py
index 16969e5545dd..a52616331a51 100644
--- a/lib/matplotlib/__init__.py
+++ b/lib/matplotlib/__init__.py
@@ -1496,6 +1496,7 @@ def _jupyter_nbextension_paths():
'matplotlib.tests.test_backend_ps',
'matplotlib.tests.test_backend_qt4',
'matplotlib.tests.test_backend_qt5',
+ 'matplotlib.tests.test_backend_gtk3',
'matplotlib.tests.test_backend_svg',
'matplotlib.tests.test_basic',
'matplotlib.tests.test_bbox_tight',
diff --git a/lib/matplotlib/backends/backend_cairo.py b/lib/matplotlib/backends/backend_cairo.py
index c567a5a2c34c..3e5ef9611cd4 100644
--- a/lib/matplotlib/backends/backend_cairo.py
+++ b/lib/matplotlib/backends/backend_cairo.py
@@ -29,25 +29,7 @@
def _fn_name(): return sys._getframe(1).f_code.co_name
-try:
- import cairocffi as cairo
-except ImportError:
- try:
- import cairo
- except ImportError:
- raise ImportError("Cairo backend requires that cairocffi or pycairo is installed.")
- else:
- HAS_CAIRO_CFFI = False
-else:
- HAS_CAIRO_CFFI = True
-
-_version_required = (1,2,0)
-if cairo.version_info < _version_required:
- raise ImportError ("Pycairo %d.%d.%d is installed\n"
- "Pycairo %d.%d.%d or later is required"
- % (cairo.version_info + _version_required))
-backend_version = cairo.version
-del _version_required
+from .cairo_compat import cairo, HAS_CAIRO_CFFI
from matplotlib.backend_bases import RendererBase, GraphicsContextBase,\
FigureManagerBase, FigureCanvasBase
diff --git a/lib/matplotlib/backends/backend_gtk3.py b/lib/matplotlib/backends/backend_gtk3.py
index 1aee5c75f590..870b239278a5 100644
--- a/lib/matplotlib/backends/backend_gtk3.py
+++ b/lib/matplotlib/backends/backend_gtk3.py
@@ -6,25 +6,7 @@
import os, sys
def fn_name(): return sys._getframe(1).f_code.co_name
-try:
- import gi
-except ImportError:
- raise ImportError("Gtk3 backend requires pygobject to be installed.")
-
-try:
- gi.require_version("Gtk", "3.0")
-except AttributeError:
- raise ImportError(
- "pygobject version too old -- it must have require_version")
-except ValueError:
- raise ImportError(
- "Gtk3 backend requires the GObject introspection bindings for Gtk 3 "
- "to be installed.")
-
-try:
- from gi.repository import Gtk, Gdk, GObject, GLib
-except ImportError:
- raise ImportError("Gtk3 backend requires pygobject to be installed.")
+from .gtk3_compat import Gtk, Gdk, GObject, GLib
import matplotlib
from matplotlib._pylab_helpers import Gcf
@@ -167,6 +149,12 @@ class FigureCanvasGTK3 (Gtk.DrawingArea, FigureCanvasBase):
65421 : 'enter',
}
+ modifier_keys = [
+ (Gdk.ModifierType.MOD4_MASK, 'super'),
+ (Gdk.ModifierType.MOD1_MASK, 'alt'),
+ (Gdk.ModifierType.CONTROL_MASK, 'ctrl'),
+ ]
+
# Setting this as a static constant prevents
# this resulting expression from leaking
event_mask = (Gdk.EventMask.BUTTON_PRESS_MASK |
@@ -293,12 +281,7 @@ def _get_key(self, event):
else:
key = None
- modifiers = [
- (Gdk.ModifierType.MOD4_MASK, 'super'),
- (Gdk.ModifierType.MOD1_MASK, 'alt'),
- (Gdk.ModifierType.CONTROL_MASK, 'ctrl'),
- ]
- for key_mask, prefix in modifiers:
+ for key_mask, prefix in self.modifier_keys:
if event.state & key_mask:
key = '{0}+{1}'.format(prefix, key)
diff --git a/lib/matplotlib/backends/backend_gtk3agg.py b/lib/matplotlib/backends/backend_gtk3agg.py
index c3eb1da68be3..e8cbb5373257 100644
--- a/lib/matplotlib/backends/backend_gtk3agg.py
+++ b/lib/matplotlib/backends/backend_gtk3agg.py
@@ -9,7 +9,7 @@
from . import backend_agg
from . import backend_gtk3
-from .backend_cairo import cairo, HAS_CAIRO_CFFI
+from .cairo_compat import cairo, HAS_CAIRO_CFFI
from matplotlib.figure import Figure
from matplotlib import transforms
@@ -46,7 +46,7 @@ def on_draw_event(self, widget, ctx):
else:
bbox_queue = self._bbox_queue
- if HAS_CAIRO_CFFI:
+ if HAS_CAIRO_CFFI and not isinstance(ctx, cairo.Context):
ctx = cairo.Context._from_pointer(
cairo.ffi.cast('cairo_t **',
id(ctx) + object.__basicsize__)[0],
diff --git a/lib/matplotlib/backends/backend_gtk3cairo.py b/lib/matplotlib/backends/backend_gtk3cairo.py
index da8f099be7f6..fe5d2d6192bf 100644
--- a/lib/matplotlib/backends/backend_gtk3cairo.py
+++ b/lib/matplotlib/backends/backend_gtk3cairo.py
@@ -5,12 +5,12 @@
from . import backend_gtk3
from . import backend_cairo
-from .backend_cairo import cairo, HAS_CAIRO_CFFI
+from .cairo_compat import cairo, HAS_CAIRO_CFFI
from matplotlib.figure import Figure
class RendererGTK3Cairo(backend_cairo.RendererCairo):
def set_context(self, ctx):
- if HAS_CAIRO_CFFI:
+ if HAS_CAIRO_CFFI and not isinstance(ctx, cairo.Context):
ctx = cairo.Context._from_pointer(
cairo.ffi.cast(
'cairo_t **',
diff --git a/lib/matplotlib/backends/cairo_compat.py b/lib/matplotlib/backends/cairo_compat.py
new file mode 100644
index 000000000000..0d6215beaaf7
--- /dev/null
+++ b/lib/matplotlib/backends/cairo_compat.py
@@ -0,0 +1,20 @@
+try:
+ import cairocffi as cairo
+except ImportError:
+ try:
+ import cairo
+ except ImportError:
+ raise ImportError(
+ "Cairo backend requires that cairocffi or pycairo is installed.")
+ else:
+ HAS_CAIRO_CFFI = False
+else:
+ HAS_CAIRO_CFFI = True
+
+_version_required = (1, 2, 0)
+if cairo.version_info < _version_required:
+ raise ImportError("Pycairo %d.%d.%d is installed\n"
+ "Pycairo %d.%d.%d or later is required"
+ % (cairo.version_info + _version_required))
+backend_version = cairo.version
+del _version_required
diff --git a/lib/matplotlib/backends/gtk3_compat.py b/lib/matplotlib/backends/gtk3_compat.py
new file mode 100644
index 000000000000..c3847041d4f6
--- /dev/null
+++ b/lib/matplotlib/backends/gtk3_compat.py
@@ -0,0 +1,34 @@
+import matplotlib
+
+error_msg_gtk3 = "Gtk3 backend requires the installation of pygobject or pgi."
+
+# Import the first library that works from the rcParam list
+# throw ImportError if none works
+for lib in matplotlib.rcParams['backend.gi_preference']:
+ try:
+ gi = __import__(lib, globals(), locals(), [], 0)
+ break
+ except ImportError:
+ pass
+else:
+ raise ImportError(error_msg_gtk3)
+
+# Check version
+try:
+ gi.require_version("Gtk", "3.0")
+except AttributeError:
+ raise ImportError(
+ "pygobject version too old -- it must have require_version")
+except ValueError:
+ raise ImportError(
+ "Gtk3 backend requires the installation of GObject introspection "
+ "bindings for Gtk 3")
+
+# cleanly import pkgs to global scope
+try:
+ pkgs = ['Gtk', 'Gdk', 'GObject', 'GLib']
+ name = gi.__name__ + '.repository'
+ _temp = __import__(name, globals(), locals(), pkgs, 0)
+ globals().update(dict((k, getattr(_temp, k)) for k in pkgs))
+except (ImportError, AttributeError):
+ raise ImportError(error_msg_gtk3)
diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py
index fcbd7adfe313..8a0c81eb406a 100644
--- a/lib/matplotlib/rcsetup.py
+++ b/lib/matplotlib/rcsetup.py
@@ -410,6 +410,9 @@ def deprecate_axes_colorcycle(value):
validate_stringlist = _listify_validator(six.text_type)
validate_stringlist.__doc__ = 'return a list'
+validate_gi_preference = _listify_validator(ValidateInStrings(
+ 'backend.gi_preference', ['gi', 'pgi']))
+
validate_orientation = ValidateInStrings(
'orientation', ['landscape', 'portrait'])
@@ -888,6 +891,7 @@ def validate_animation_writer_path(p):
'backend_fallback': [True, validate_bool], # agg is certainly present
'backend.qt4': ['PyQt4', validate_qt4],
'backend.qt5': ['PyQt5', validate_qt5],
+ 'backend.gi_preference': [['gi', 'pgi'], validate_gi_preference],
'webagg.port': [8988, validate_int],
'webagg.open_in_browser': [True, validate_bool],
'webagg.port_retries': [50, validate_int],
diff --git a/lib/matplotlib/tests/test_backend_gtk3.py b/lib/matplotlib/tests/test_backend_gtk3.py
new file mode 100644
index 000000000000..47953432f7e6
--- /dev/null
+++ b/lib/matplotlib/tests/test_backend_gtk3.py
@@ -0,0 +1,60 @@
+from __future__ import (absolute_import, division, print_function,
+ unicode_literals)
+
+from matplotlib.externals import six
+from matplotlib.externals.six import unichr
+from matplotlib import pyplot as plt
+from matplotlib.testing.decorators import cleanup, switch_backend
+from matplotlib.testing.decorators import knownfailureif
+from matplotlib._pylab_helpers import Gcf
+import copy
+
+try:
+ # mock in python 3.3+
+ from unittest import mock
+except ImportError:
+ import mock
+
+try:
+ from matplotlib.backends.gtk3_compat import Gtk, Gdk, GObject, GLib
+ HAS_GTK3 = True
+except ImportError:
+ HAS_GTK3 = False
+
+
+def simulate_key_press(canvas, key, modifiers=[]):
+ event = mock.Mock()
+
+ keyval = [k for k, v in six.iteritems(canvas.keyvald) if v == key]
+ if keyval:
+ keyval = keyval[0]
+ else:
+ keyval = ord(key)
+ event.keyval = keyval
+
+ event.state = 0
+ for key_mask, prefix in canvas.modifier_keys:
+ if prefix in modifiers:
+ event.state |= key_mask
+
+ canvas.key_press_event(None, event)
+
+
+@cleanup
+#@knownfailureif(not HAS_GTK3)
+@switch_backend('GTK3Agg')
+def test_fig_close():
+ #save the state of Gcf.figs
+ init_figs = copy.copy(Gcf.figs)
+
+ # make a figure using pyplot interface
+ fig = plt.figure()
+
+ # simulate user pressing the close shortcut
+ #simulate_key_press(fig.canvas, 'w', ['ctrl'])
+
+ plt.show()
+
+ # assert that we have removed the reference to the FigureManager
+ # that got added by plt.figure()
+ assert(init_figs == Gcf.figs)
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy