diff --git a/.travis.yml b/.travis.yml
index 56901a44412d..c6971c322498 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -19,22 +19,25 @@ addons:
- result_images.tar.bz2
apt:
packages:
+ - cm-super
+ - dvipng
+ - gdb
+ - gir1.2-gtk-3.0
+ - graphviz
- inkscape
- libav-tools
- - gdb
+ - libcairo2
+ - libgeos-dev
+ - libgirepository-1.0.1
+ - lmodern
- mencoder
- - dvipng
+ - otf-freefont
- pgf
- - lmodern
- - cm-super
+ - texlive-fonts-recommended
- texlive-latex-base
- texlive-latex-extra
- - texlive-fonts-recommended
- texlive-latex-recommended
- texlive-xetex
- - graphviz
- - libgeos-dev
- - otf-freefont
env:
global:
@@ -123,6 +126,10 @@ install:
# install was successful by trying to import the toolkit (sometimes, the
# install appears to be successful but shared libraries cannot be loaded at
# runtime, so an actual import is a better check).
+ pip install cairocffi pgi &&
+ python -c 'import pgi as gi; gi.require_version("Gtk", "3.0"); from pgi.repository import Gtk' &&
+ echo 'pgi is available' ||
+ echo 'pgi is not available'
pip install pyqt5 &&
python -c 'import PyQt5.QtCore' &&
echo 'PyQt5 is available' ||
diff --git a/doc/glossary/index.rst b/doc/glossary/index.rst
index a4713567726a..487caed10f4a 100644
--- a/doc/glossary/index.rst
+++ b/doc/glossary/index.rst
@@ -8,8 +8,8 @@ Glossary
.. glossary::
AGG
- The Anti-Grain Geometry (`Agg `_) rendering engine, capable of rendering
- high-quality images
+ The Anti-Grain Geometry (`Agg `_) rendering
+ engine, capable of rendering high-quality images
Cairo
The `Cairo graphics `_ engine
@@ -20,7 +20,8 @@ Glossary
provides extensions to the standard datetime module
EPS
- Encapsulated Postscript (`EPS `_)
+ Encapsulated Postscript (`EPS
+ `_)
FreeType
`FreeType `_ is a font rasterization
@@ -32,7 +33,8 @@ Glossary
The Gimp Drawing Kit for GTK+
GTK
- The GIMP Toolkit (`GTK `_) graphical user interface library
+ The GIMP Toolkit (`GTK `_) graphical user interface
+ library
JPG
The Joint Photographic Experts Group (`JPEG
@@ -47,13 +49,14 @@ Glossary
deviation, fourier transforms, and convolutions.
PDF
- Adobe's Portable Document Format (`PDF `_)
+ Adobe's Portable Document Format (`PDF
+ `_)
PNG
Portable Network Graphics (`PNG
- `_), a raster graphics format
- that employs lossless data compression which is more suitable
- for line art than the lossy jpg format. Unlike the gif format,
+ `_), a raster
+ graphics format that employs lossless data compression which is more
+ suitable for line art than the lossy jpg format. Unlike the gif format,
png is not encumbered by requirements for a patent license.
PS
@@ -64,12 +67,24 @@ 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.
+
pygtk
`pygtk `_ provides python wrappers for
the :term:`GTK` widgets library for use with the GTK or GTKAgg
backend. Widely used on linux, and is often packages as
'python-gtk2'
+ 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.
+
pyqt
`pyqt `_ provides python
wrappers for the :term:`Qt` widgets library and is required by
@@ -82,14 +97,12 @@ Glossary
language widely used for scripting, application development, web
application servers, scientific computing and more.
-
pytz
`pytz `_ provides the Olson tz
database in Python. it allows accurate and cross platform
timezone calculations and solves the issue of ambiguous times at
the end of daylight savings
-
Qt
`Qt `__ is a cross-platform
application framework for desktop and embedded development.
diff --git a/doc/users/next_whats_new/2017-12-04-AL-pgi.rst b/doc/users/next_whats_new/2017-12-04-AL-pgi.rst
new file mode 100644
index 000000000000..4f774bef6989
--- /dev/null
+++ b/doc/users/next_whats_new/2017-12-04-AL-pgi.rst
@@ -0,0 +1,19 @@
+PGI bindings for gtk3
+---------------------
+
+The GTK3 backends can now use PGI_ instead of PyGObject_. PGI is a fairly
+incomplete binding for GObject, thus its use is not recommended; its main
+benefit is its availability on Travis (thus allowing CI testing for the gtk3agg
+and gtk3cairo backends).
+
+The binding selection rules are as follows:
+- if ``gi`` has already been imported, use it; else
+- if ``pgi`` has already been imported, use it; else
+- if ``gi`` can be imported, use it; else
+- if ``pgi`` can be imported, use it; else
+- error out.
+
+Thus, to force usage of PGI when both bindings are installed, import it first.
+
+.. _PGI: https://pgi.readthedocs.io/en/latest/
+.. _PyGObject: http://pygobject.readthedocs.io/en/latest/#
diff --git a/lib/matplotlib/backends/_gtk3_compat.py b/lib/matplotlib/backends/_gtk3_compat.py
new file mode 100644
index 000000000000..825fa2341c80
--- /dev/null
+++ b/lib/matplotlib/backends/_gtk3_compat.py
@@ -0,0 +1,41 @@
+"""
+GObject compatibility loader; supports ``gi`` and ``pgi``.
+
+The binding selection rules are as follows:
+- if ``gi`` has already been imported, use it; else
+- if ``pgi`` has already been imported, use it; else
+- if ``gi`` can be imported, use it; else
+- if ``pgi`` can be imported, use it; else
+- error out.
+
+Thus, to force usage of PGI when both bindings are installed, import it first.
+"""
+
+from __future__ import (absolute_import, division, print_function,
+ unicode_literals)
+
+import six
+
+import importlib
+import sys
+
+
+if "gi" in sys.modules:
+ import gi
+elif "pgi" in sys.modules:
+ import pgi as gi
+else:
+ try:
+ import gi
+ except ImportError:
+ try:
+ import pgi as gi
+ except ImportError:
+ raise ImportError("The Gtk3 backend requires PyGObject or pgi")
+
+
+gi.require_version("Gtk", "3.0")
+globals().update(
+ {name:
+ importlib.import_module("{}.repository.{}".format(gi.__name__, name))
+ for name in ["GLib", "GObject", "Gtk", "Gdk"]})
diff --git a/lib/matplotlib/backends/backend_gtk3.py b/lib/matplotlib/backends/backend_gtk3.py
index 3e1225b8a0b1..606b01977cdd 100644
--- a/lib/matplotlib/backends/backend_gtk3.py
+++ b/lib/matplotlib/backends/backend_gtk3.py
@@ -7,39 +7,20 @@
import os
import sys
-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.")
-
import matplotlib
+from matplotlib import (
+ backend_tools, cbook, colors as mcolors, lines, rcParams)
from matplotlib._pylab_helpers import Gcf
from matplotlib.backend_bases import (
_Backend, FigureCanvasBase, FigureManagerBase, GraphicsContextBase,
- NavigationToolbar2, RendererBase, TimerBase, cursors)
-from matplotlib.backend_bases import ToolContainerBase, StatusbarBase
+ NavigationToolbar2, RendererBase, StatusbarBase, TimerBase,
+ ToolContainerBase, cursors)
from matplotlib.backend_managers import ToolManager
from matplotlib.cbook import is_writable_file_like
from matplotlib.figure import Figure
from matplotlib.widgets import SubplotTool
+from ._gtk3_compat import GLib, GObject, Gtk, Gdk
-from matplotlib import (
- backend_tools, cbook, colors as mcolors, lines, rcParams)
_log = logging.getLogger(__name__)
diff --git a/lib/matplotlib/backends/backend_gtk3agg.py b/lib/matplotlib/backends/backend_gtk3agg.py
index a8bce0c4e6fc..5d175ec7eccc 100644
--- a/lib/matplotlib/backends/backend_gtk3agg.py
+++ b/lib/matplotlib/backends/backend_gtk3agg.py
@@ -42,7 +42,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 79ba1fc2d24d..f27d38ecdb26 100644
--- a/lib/matplotlib/backends/backend_gtk3cairo.py
+++ b/lib/matplotlib/backends/backend_gtk3cairo.py
@@ -12,7 +12,7 @@
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/tests/test_backends_interactive.py b/lib/matplotlib/tests/test_backends_interactive.py
index 0a8b7d80276c..07d5759bbe0c 100644
--- a/lib/matplotlib/tests/test_backends_interactive.py
+++ b/lib/matplotlib/tests/test_backends_interactive.py
@@ -17,17 +17,22 @@
def _get_testable_interactive_backends():
- return [
- pytest.mark.skipif(
- not os.environ.get("DISPLAY")
- or sys.version_info < (3,)
- or importlib.util.find_spec(module_name) is None,
- reason="No $DISPLAY or could not import {!r}".format(module_name))(
- backend)
- for module_name, backend in [
- ("PyQt5", "qt5agg"),
- ("tkinter", "tkagg"),
- ("wx", "wxagg")]]
+ backends = []
+ for deps, backend in [(["cairocffi", "pgi"], "gtk3agg"),
+ (["cairocffi", "pgi"], "gtk3cairo"),
+ (["PyQt5"], "qt5agg"),
+ (["tkinter"], "tkagg"),
+ (["wx"], "wxagg")]:
+ reason = None
+ if sys.version_info < (3,):
+ reason = "Py3-only test"
+ elif not os.environ.get("DISPLAY"):
+ reason = "No $DISPLAY"
+ elif any(importlib.util.find_spec(dep) is None for dep in deps):
+ reason = "Missing dependency"
+ backends.append(pytest.mark.skip(reason=reason)(backend) if reason
+ else backend)
+ return backends
_test_script = """\