From cf819b6e983806908270aa81d431da5bbac77079 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Mon, 20 May 2013 13:23:39 -0400 Subject: [PATCH 01/11] Deprecate the top-level functions in path.py --- doc/api/api_changes.rst | 16 +++++ lib/matplotlib/cbook.py | 121 +++++++++++++++++++++++++++++++++- lib/matplotlib/collections.py | 3 +- lib/matplotlib/path.py | 61 ++++++++++++----- 4 files changed, 182 insertions(+), 19 deletions(-) diff --git a/doc/api/api_changes.rst b/doc/api/api_changes.rst index e93a773a7908..748457a95b87 100644 --- a/doc/api/api_changes.rst +++ b/doc/api/api_changes.rst @@ -15,6 +15,22 @@ For new features that were added to matplotlib, please see Changes in 1.3.x ================ +* The top-level functions in `matplotlib.path` that are implemented in + C++ were never meant to be public. Instead, users should use the + Pythonic wrappers for them in the `path.Path` and + `collections.Collection` classes. Use the following mapping to update + your code: + + - `point_in_path` -> `path.Path.contains_point` + - `get_path_extents` -> `path.Path.get_extents` + - `point_in_path_collection` -> `collection.Collection.contains` + - `path_in_path` -> `path.Path.contains_path` + - `path_intersects_path` -> `path.Path.intersects_path` + - `convert_path_to_polygons` -> `path.Path.to_polygons` + - `cleanup_path` -> `path.Path.cleaned` + - `points_in_path` -> `path.Path.contains_points` + - `clip_path_to_rect` -> `path.Path.clip_to_bbox` + * `Path` objects can now be marked as `readonly` by passing `readonly=True` to its constructor. The built-in path singletons, obtained through `Path.unit*` class methods return readonly paths. diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index da44e47f2085..911e606108bc 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -1,5 +1,5 @@ """ -A collection of utility functions and classes. Originally, many +A collection of utility functions and classes. Originally, many (but not all) were from the Python Cookbook -- hence the name cbook. This module is safe to import from anywhere within matplotlib; @@ -44,6 +44,125 @@ class MatplotlibDeprecationWarning(UserWarning): mplDeprecation = MatplotlibDeprecationWarning + +def deprecated(since, message='', name='', alternative='', pending=False, + obj_type='function'): + """ + Used to mark a function as deprecated. + + Parameters + ------------ + since : str + The release at which this API became deprecated. This is + required. + + message : str, optional + Override the default deprecation message. The format + specifier `%(func)s` may be used for the name of the function, + and `%(alternative)s` may be used in the deprecation message + to insert the name of an alternative to the deprecated + function. `%(obj_type)` may be used to insert a friendly name + for the type of object being deprecated. + + name : str, optional + The name of the deprecated function; if not provided the name + is automatically determined from the passed in function, + though this is useful in the case of renamed functions, where + the new function is just assigned to the name of the + deprecated function. For example:: + + def new_function(): + ... + oldFunction = new_function + + alternative : str, optional + An alternative function that the user may use in place of the + deprecated function. The deprecation warning will tell the user about + this alternative if provided. + + pending : bool, optional + If True, uses a PendingDeprecationWarning instead of a + DeprecationWarning. + """ + + def deprecate(func, message=message, name=name, alternative=alternative, + pending=pending): + import functools + import textwrap + + if isinstance(func, classmethod): + try: + func = func.__func__ + except AttributeError: + # classmethods in Python2.6 and below lack the __func__ + # attribute so we need to hack around to get it + method = func.__get__(None, object) + if hasattr(method, '__func__'): + func = method.__func__ + elif hasattr(method, 'im_func'): + func = method.im_func + else: + # Nothing we can do really... just return the original + # classmethod + return func + is_classmethod = True + else: + is_classmethod = False + + if not name: + name = func.__name__ + + altmessage = '' + if not message or type(message) == type(deprecate): + if pending: + message = ('The %(func)s %(obj_type)s will be deprecated in a ' + 'future version.') + else: + message = ('The %(func)s %(obj_type)s is deprecated and may ' + 'be removed in a future version.') + if alternative: + altmessage = '\n Use %s instead.' % alternative + + message = ((message % { + 'func': name, + 'name': name, + 'alternative': alternative, + 'obj_type': obj_type}) + + altmessage) + + @functools.wraps(func) + def deprecated_func(*args, **kwargs): + warnings.warn(message, mplDeprecation, stacklevel=2) + + return func(*args, **kwargs) + + old_doc = deprecated_func.__doc__ + if not old_doc: + old_doc = '' + old_doc = textwrap.dedent(old_doc).strip('\n') + altmessage = altmessage.strip() + if not altmessage: + altmessage = message.strip() + new_doc = (('\n.. deprecated:: %(since)s' + '\n %(message)s\n\n' % + {'since': since, 'message': altmessage.strip()}) + old_doc) + if not old_doc: + # This is to prevent a spurious 'unexected unindent' warning from + # docutils when the original docstring was blank. + new_doc += r'\ ' + + deprecated_func.__doc__ = new_doc + + if is_classmethod: + deprecated_func = classmethod(deprecated_func) + return deprecated_func + + if type(message) == type(deprecate): + return deprecate(message) + + return deprecate + + # On some systems, locale.getpreferredencoding returns None, # which can break unicode; and the sage project reports that # some systems have incorrect locale specifications, e.g., diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index b2a5c3e7265d..66908ef2a8e1 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -22,6 +22,7 @@ from matplotlib.artist import allow_rasterization import matplotlib.backend_bases as backend_bases import matplotlib.path as mpath +from matplotlib import _path import matplotlib.mlab as mlab @@ -315,7 +316,7 @@ def contains(self, mouseevent): transform, transOffset, offsets, paths = self._prepare_points() - ind = mpath.point_in_path_collection( + ind = _path.point_in_path_collection( mouseevent.x, mouseevent.y, pickradius, transform.frozen(), paths, self.get_transforms(), offsets, transOffset, pickradius <= 0, diff --git a/lib/matplotlib/path.py b/lib/matplotlib/path.py index 6f0e270a514c..4fff2807936f 100644 --- a/lib/matplotlib/path.py +++ b/lib/matplotlib/path.py @@ -9,13 +9,16 @@ import numpy as np from numpy import ma -from matplotlib._path import point_in_path, get_path_extents, \ - point_in_path_collection, get_path_collection_extents, \ - path_in_path, path_intersects_path, convert_path_to_polygons, \ - cleanup_path, points_in_path, clip_path_to_rect +from matplotlib import _path + +# ._path import point_in_path, get_path_extents, \ +# point_in_path_collection, get_path_collection_extents, \ +# path_in_path, path_intersects_path, convert_path_to_polygons, \ +# cleanup_path, points_in_path, clip_path_to_rect from matplotlib.cbook import simple_linear_interpolation, maxdict from matplotlib import rcParams + class Path(object): """ :class:`Path` represents a series of possibly disconnected, @@ -356,9 +359,10 @@ def iter_segments(self, transform=None, remove_nans=True, clip=None, CLOSEPOLY = self.CLOSEPOLY STOP = self.STOP - vertices, codes = cleanup_path(self, transform, remove_nans, clip, - snap, stroke_width, simplify, curves, - sketch) + vertices, codes = _path.cleanup_path( + self, transform, remove_nans, clip, + snap, stroke_width, simplify, curves, + sketch) len_vertices = len(vertices) i = 0 @@ -398,7 +402,7 @@ def contains_point(self, point, transform=None, radius=0.0): """ if transform is not None: transform = transform.frozen() - result = point_in_path(point[0], point[1], radius, self, transform) + result = _path.point_in_path(point[0], point[1], radius, self, transform) return result def contains_points(self, points, transform=None, radius=0.0): @@ -414,7 +418,7 @@ def contains_points(self, points, transform=None, radius=0.0): """ if transform is not None: transform = transform.frozen() - result = points_in_path(points, radius, self, transform) + result = _path.points_in_path(points, radius, self, transform) return result def contains_path(self, path, transform=None): @@ -426,7 +430,7 @@ def contains_path(self, path, transform=None): """ if transform is not None: transform = transform.frozen() - return path_in_path(self, None, path, transform) + return _path.path_in_path(self, None, path, transform) def get_extents(self, transform=None): """ @@ -444,7 +448,7 @@ def get_extents(self, transform=None): if not transform.is_affine: path = self.transformed(transform) transform = None - return Bbox(get_path_extents(path, transform)) + return Bbox(_path.get_path_extents(path, transform)) def intersects_path(self, other, filled=True): """ @@ -454,7 +458,7 @@ def intersects_path(self, other, filled=True): That is, if one path completely encloses the other, :meth:`intersects_path` will return True. """ - return path_intersects_path(self, other, filled) + return _path.path_intersects_path(self, other, filled) def intersects_bbox(self, bbox, filled=True): """ @@ -514,7 +518,7 @@ def to_polygons(self, transform=None, width=0, height=0): # Deal with the case where there are curves and/or multiple # subpaths (using extension code) - return convert_path_to_polygons(self, transform, width, height) + return _path.convert_path_to_polygons(self, transform, width, height) _unit_rectangle = None @classmethod @@ -833,12 +837,11 @@ def clip_to_bbox(self, bbox, inside=True): to the outside of the box. """ # Use make_compound_path_from_polys - verts = clip_path_to_rect(self, bbox, inside) + verts = _path.clip_path_to_rect(self, bbox, inside) paths = [Path(poly) for poly in verts] return self.make_compound_path(*paths) -_get_path_collection_extents = get_path_collection_extents def get_path_collection_extents( master_transform, paths, transforms, offsets, offset_transform): """ @@ -869,9 +872,10 @@ def get_path_collection_extents( from transforms import Bbox if len(paths) == 0: raise ValueError("No paths provided") - return Bbox.from_extents(*_get_path_collection_extents( + return Bbox.from_extents(*_path.get_path_collection_extents( master_transform, paths, transforms, offsets, offset_transform)) + def get_paths_extents(paths, transforms=[]): """ Given a sequence of :class:`Path` objects and optional @@ -887,5 +891,28 @@ def get_paths_extents(paths, transforms=[]): from transforms import Bbox, Affine2D if len(paths) == 0: raise ValueError("No paths provided") - return Bbox.from_extents(*_get_path_collection_extents( + return Bbox.from_extents(*_path.get_path_collection_extents( Affine2D(), paths, transforms, [], Affine2D())) + + +def _define_deprecated_functions(ns): + from cbook import deprecated + + # The C++ functions are not meant to be used directly. + # Users should use the more pythonic wrappers in the Path + # class instead. + for func, alternative in [ + ('point_in_path', 'path.Path.contains_point'), + ('get_path_extents', 'path.Path.get_extents'), + ('point_in_path_collection', 'collection.Collection.contains'), + ('path_in_path', 'path.Path.contains_path'), + ('path_intersects_path', 'path.Path.intersects_path'), + ('convert_path_to_polygons', 'path.Path.to_polygons'), + ('cleanup_path', 'path.Path.cleaned'), + ('points_in_path', 'path.Path.contains_points'), + ('clip_path_to_rect', 'path.Path.clip_to_bbox')]: + ns[func] = deprecated( + since='1.3', alternative=alternative)(getattr(_path, func)) + + +_define_deprecated_functions(locals()) From 84ffb605d4dd06de0dc7eebb2605a35596c9e092 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Mon, 20 May 2013 14:28:04 -0400 Subject: [PATCH 02/11] Remove deprecated code --- doc/api/api_changes.rst | 46 ++ doc/faq/usage_faq.rst | 5 +- doc/users/screenshots.rst | 2 +- .../old_animation/animation_blit_qt.py | 66 --- examples/event_handling/pipong.py | 1 - examples/event_handling/pong_qt.py | 40 -- examples/event_handling/test_mouseclicks.py | 1 - examples/user_interfaces/embedding_in_qt.py | 137 ----- lib/matplotlib/__init__.py | 5 - lib/matplotlib/axes.py | 10 - lib/matplotlib/backends/backend_qt.py | 498 ------------------ lib/matplotlib/backends/backend_qtagg.py | 158 ------ lib/matplotlib/backends/backend_wx.py | 190 +------ lib/matplotlib/legend.py | 27 +- lib/matplotlib/mlab.py | 180 ------- lib/matplotlib/nxutils.py | 51 -- lib/matplotlib/patches.py | 7 - lib/matplotlib/pylab.py | 25 +- lib/matplotlib/pyplot.py | 5 - lib/matplotlib/rcsetup.py | 2 +- lib/matplotlib/tests/test_png.py | 2 +- lib/matplotlib/widgets.py | 9 - matplotlibrc.template | 7 +- setup.py | 1 - setupext.py | 30 -- unit/memleak_gui.py | 5 - 26 files changed, 57 insertions(+), 1453 deletions(-) delete mode 100644 examples/animation/old_animation/animation_blit_qt.py delete mode 100644 examples/event_handling/pong_qt.py delete mode 100755 examples/user_interfaces/embedding_in_qt.py delete mode 100644 lib/matplotlib/backends/backend_qt.py delete mode 100644 lib/matplotlib/backends/backend_qtagg.py delete mode 100644 lib/matplotlib/nxutils.py diff --git a/doc/api/api_changes.rst b/doc/api/api_changes.rst index 748457a95b87..219c2a5d7553 100644 --- a/doc/api/api_changes.rst +++ b/doc/api/api_changes.rst @@ -15,6 +15,52 @@ For new features that were added to matplotlib, please see Changes in 1.3.x ================ +* The following items that were deprecated in version 1.2 or earlier + have now been removed completely. Use the following mapping to + update your code: + + - The Qt 3.x backends (`qt` and `qtagg`) have been removed in + favor of the Qt 4.x backends (`qt4` and `qt4agg`). + + - The `matplotlib.nxutils` module has been removed. Use the + functionality on `matplotlib.path.Path.contains_point` and + friends instead. + + - Instead of `axes.Axes.get_frame`, use `axes.Axes.patch`. + + - The following `kwargs` to the `legend` function have been + renamed: + + - `pad` -> `borderpad` + - `labelsep` -> `labelspacing` + - `handlelen` -> `handlelength` + - `handletextsep` -> `handletextpad` + - `axespad` -> `borderaxespad` + + Related to this, the following rcParams have been removed: + + - `legend.pad`, `legend.labelsep`, `legend.handlelen`, + `legend.handletextsep` and `legend.axespad` + + - For the `hist` function, instead of `width`, use `rwidth` + (relative width). + + - On `patches.Circle`, the `resolution` kwarg has been removed. + For a circle made up of line segments, use + `patches.CirclePolygon`. + + - The printing functions in the Wx backend have been removed due + to the burden of keeping them up-to-date. + + - `mlab.liaupunov` has been removed. + + - `mlab.save`, `mlab.load`, `pylab.save` and `pylab.load` have + been removed. We recommend using `numpy.savetxt` and + `numpy.loadtxt` instead. + + - `widgets.HorizontalSpanSelector` has been removed. Use + `widgets.SpanSelector` instead. + * The top-level functions in `matplotlib.path` that are implemented in C++ were never meant to be public. Instead, users should use the Pythonic wrappers for them in the `path.Path` and diff --git a/doc/faq/usage_faq.rst b/doc/faq/usage_faq.rst index e107c1e6ae7e..98af2a3aaece 100644 --- a/doc/faq/usage_faq.rst +++ b/doc/faq/usage_faq.rst @@ -166,7 +166,7 @@ outputs, and each of these capabilities is called a backend; the "frontend" is the user facing code, ie the plotting code, whereas the "backend" does all the hard work behind-the-scenes to make the figure. There are two types of backends: user interface backends (for -use in pygtk, wxpython, tkinter, qt, macosx, or fltk; also +use in pygtk, wxpython, tkinter, qt4, macosx, or fltk; also referred to as "interactive backends") and hardcopy backends to make image files (PNG, SVG, PDF, PS; also referred to as "non-interactive backends"). @@ -266,8 +266,6 @@ WXAgg Agg rendering to to a :term:`wxWidgets` canvas WX Native :term:`wxWidgets` drawing to a :term:`wxWidgets` Canvas (not recommended) (requires wxPython_) TkAgg Agg rendering to a :term:`Tk` canvas (requires TkInter_) -QtAgg Agg rendering to a :term:`Qt` canvas (requires PyQt_) - (deprecated; use Qt4Agg) Qt4Agg Agg rendering to a :term:`Qt4` canvas (requires PyQt4_) FLTKAgg Agg rendering to a :term:`FLTK` canvas (requires pyFLTK_) (not widely used; consider TKAgg, GTKAgg, WXAgg, or @@ -288,7 +286,6 @@ macosx Cocoa rendering in OSX windows .. _pycairo: http://www.cairographics.org/pycairo/ .. _wxPython: http://www.wxpython.org/ .. _TkInter: http://wiki.python.org/moin/TkInter -.. _PyQt: http://www.riverbankcomputing.co.uk/software/pyqt/intro .. _PyQt4: http://www.riverbankcomputing.co.uk/software/pyqt/intro .. _pyFLTK: http://pyfltk.sourceforge.net diff --git a/doc/users/screenshots.rst b/doc/users/screenshots.rst index e56f31d656ee..ba260701a0b0 100644 --- a/doc/users/screenshots.rst +++ b/doc/users/screenshots.rst @@ -265,7 +265,7 @@ rendering of strings with the *usetex* option. EEG demo ========= -You can embed matplotlib into pygtk, wxpython, Tk, FLTK or Qt +You can embed matplotlib into pygtk, wxpython, Tk, FLTK or Qt4 applications. Here is a screenshot of an eeg viewer called pbrain which is part of the NeuroImaging in Python suite `NIPY `_. Pbrain is written in pygtk using diff --git a/examples/animation/old_animation/animation_blit_qt.py b/examples/animation/old_animation/animation_blit_qt.py deleted file mode 100644 index 272997315e93..000000000000 --- a/examples/animation/old_animation/animation_blit_qt.py +++ /dev/null @@ -1,66 +0,0 @@ -# For detailed comments on animation and the techniqes used here, see -# the wiki entry http://www.scipy.org/Cookbook/Matplotlib/Animations - -from __future__ import print_function - -import os, sys -import matplotlib -matplotlib.use('QtAgg') # qt3 example - -from qt import * -# Note: color-intensive applications may require a different color allocation -# strategy. -QApplication.setColorSpec(QApplication.NormalColor) - -TRUE = 1 -FALSE = 0 -ITERS = 1000 - -import pylab as p -import numpy as npy -import time - -class BlitQT(QObject): - def __init__(self): - QObject.__init__(self, None, "app") - - self.ax = p.subplot(111) - self.canvas = self.ax.figure.canvas - self.cnt = 0 - - # create the initial line - self.x = npy.arange(0,2*npy.pi,0.01) - self.line, = p.plot(self.x, npy.sin(self.x), animated=True, lw=2) - - self.background = None - - def timerEvent(self, evt): - if self.background is None: - self.background = self.canvas.copy_from_bbox(self.ax.bbox) - - # restore the clean slate background - self.canvas.restore_region(self.background) - # update the data - self.line.set_ydata(npy.sin(self.x+self.cnt/10.0)) - # just draw the animated artist - self.ax.draw_artist(self.line) - # just redraw the axes rectangle - self.canvas.blit(self.ax.bbox) - - if self.cnt==ITERS: - # print the timing info and quit - print('FPS:', ITERS/(time.time()-self.tstart)) - sys.exit() - - else: - self.cnt += 1 - -p.subplots_adjust(left=0.3, bottom=0.3) # check for flipy bugs -p.grid() # to ensure proper background restore - -app = BlitQT() -# for profiling -app.tstart = time.time() -app.startTimer(0) - -p.show() diff --git a/examples/event_handling/pipong.py b/examples/event_handling/pipong.py index 7433aaf2c463..be1352218f07 100755 --- a/examples/event_handling/pipong.py +++ b/examples/event_handling/pipong.py @@ -250,7 +250,6 @@ def key_press(self,event): if event.key == 'g': #self.ax.clear() - #self.ax.grid() # seems to be necessary for qt backend self.on = not self.on if event.key == 't': self.inst = not self.inst diff --git a/examples/event_handling/pong_qt.py b/examples/event_handling/pong_qt.py deleted file mode 100644 index d27b6c1101e7..000000000000 --- a/examples/event_handling/pong_qt.py +++ /dev/null @@ -1,40 +0,0 @@ -# For detailed comments on animation and the techniqes used here, see -# the wiki entry http://www.scipy.org/Cookbook/Matplotlib/Animations - -from __future__ import print_function - -import matplotlib -matplotlib.use('QtAgg') # qt3 example - -from qt import * -# Note: color-intensive applications may require a different color allocation -# strategy. -QApplication.setColorSpec(QApplication.NormalColor) - -TRUE = 1 -FALSE = 0 -ITERS = 1000 - -import matplotlib.pyplot as plt -import time -import pipong - -class BlitQT(QObject): - def __init__(self): - QObject.__init__(self, None, "app") - - self.ax = plt.subplot(111) - self.animation = pipong.Game(self.ax) - - def timerEvent(self, evt): - self.animation.draw(evt) - -plt.grid() # to ensure proper background restore - -app = BlitQT() -# for profiling -app.tstart = time.time() -app.startTimer(10) - -plt.show() -print('FPS:' , app.animation.cnt/(time.time()-app.tstart)) diff --git a/examples/event_handling/test_mouseclicks.py b/examples/event_handling/test_mouseclicks.py index 450d2c63d628..506ee5b1cd7b 100755 --- a/examples/event_handling/test_mouseclicks.py +++ b/examples/event_handling/test_mouseclicks.py @@ -5,7 +5,6 @@ #matplotlib.use("WxAgg") #matplotlib.use("TkAgg") #matplotlib.use("GTKAgg") -#matplotlib.use("QtAgg") #matplotlib.use("Qt4Agg") #matplotlib.use("CocoaAgg") #matplotlib.use("MacOSX") diff --git a/examples/user_interfaces/embedding_in_qt.py b/examples/user_interfaces/embedding_in_qt.py deleted file mode 100755 index 311fd9e961f7..000000000000 --- a/examples/user_interfaces/embedding_in_qt.py +++ /dev/null @@ -1,137 +0,0 @@ -#! /usr/bin/env python - -# embedding_in_qt.py --- Simple Qt application embedding matplotlib canvases -# -# Copyright (C) 2005 Florent Rougon -# -# This file is an example program for matplotlib. It may be used and -# modified with no restriction; raw copies as well as modified versions -# may be distributed without limitation. - -from __future__ import unicode_literals -import sys, os, random -from qt import * - -from numpy import arange, sin, pi -from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas -from matplotlib.figure import Figure - -# This seems to be what PyQt expects, according to the examples shipped in -# its distribution. -TRUE = 1 -FALSE = 0 - -progname = os.path.basename(sys.argv[0]) -progversion = "0.1" - -# Note: color-intensive applications may require a different color allocation -# strategy. -#QApplication.setColorSpec(QApplication.NormalColor) -app = QApplication(sys.argv) - -class MyMplCanvas(FigureCanvas): - """Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.).""" - def __init__(self, parent=None, width=5, height=4, dpi=100): - self.fig = Figure(figsize=(width, height), dpi=dpi) - self.axes = self.fig.add_subplot(111) - # We want the axes cleared every time plot() is called - self.axes.hold(False) - - self.compute_initial_figure() - - FigureCanvas.__init__(self, self.fig) - self.reparent(parent, QPoint(0, 0)) - - FigureCanvas.setSizePolicy(self, - QSizePolicy.Expanding, - QSizePolicy.Expanding) - FigureCanvas.updateGeometry(self) - - def sizeHint(self): - w, h = self.get_width_height() - return QSize(w, h) - - def minimumSizeHint(self): - return QSize(10, 10) - - -class MyStaticMplCanvas(MyMplCanvas): - """Simple canvas with a sine plot.""" - def compute_initial_figure(self): - t = arange(0.0, 3.0, 0.01) - s = sin(2*pi*t) - self.axes.plot(t, s) - - -class MyDynamicMplCanvas(MyMplCanvas): - """A canvas that updates itself every second with a new plot.""" - def __init__(self, *args, **kwargs): - MyMplCanvas.__init__(self, *args, **kwargs) - timer = QTimer(self, "canvas update timer") - QObject.connect(timer, SIGNAL("timeout()"), self.update_figure) - timer.start(1000, FALSE) - - def compute_initial_figure(self): - self.axes.plot([0, 1, 2, 3], [1, 2, 0, 4], 'r') - - def update_figure(self): - # Build a list of 4 random integers between 0 and 10 (both inclusive) - l = [ random.randint(0, 10) for i in range(4) ] - - self.axes.plot([0, 1, 2, 3], l, 'r') - self.draw() - - -class ApplicationWindow(QMainWindow): - def __init__(self): - QMainWindow.__init__(self, None, - "application main window", - Qt.WType_TopLevel | Qt.WDestructiveClose) - - self.file_menu = QPopupMenu(self) - self.file_menu.insertItem('&Quit', self.fileQuit, Qt.CTRL + Qt.Key_Q) - self.menuBar().insertItem('&File', self.file_menu) - - self.help_menu = QPopupMenu(self) - self.menuBar().insertSeparator() - self.menuBar().insertItem('&Help', self.help_menu) - - self.help_menu.insertItem('&About', self.about) - - self.main_widget = QWidget(self, "Main widget") - - l = QVBoxLayout(self.main_widget) - sc = MyStaticMplCanvas(self.main_widget, width=5, height=4, dpi=100) - dc = MyDynamicMplCanvas(self.main_widget, width=5, height=4, dpi=100) - l.addWidget(sc) - l.addWidget(dc) - - self.main_widget.setFocus() - self.setCentralWidget(self.main_widget) - - self.statusBar().message("All hail matplotlib!", 2000) - - def fileQuit(self): - qApp.exit(0) - - def closeEvent(self, ce): - self.fileQuit() - - def about(self): - QMessageBox.about(self, "About %s" % progname, -"""%(prog)s version %(version)s -Copyright \N{COPYRIGHT SIGN} 2005 Florent Rougon - -This program is a simple example of a Qt application embedding matplotlib -canvases. - -It may be used and modified with no restriction; raw copies as well as -modified versions may be distributed without limitation.""" - % {"prog": progname, "version": progversion}) - - -aw = ApplicationWindow() -aw.setCaption("%s" % progname) -qApp.setMainWidget(aw) -aw.show() -sys.exit(qApp.exec_loop()) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index c94ec885742e..c8d0eb1543c2 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -719,11 +719,6 @@ def matplotlib_fname(): } _deprecated_ignore_map = { - 'legend.pad' : 'legend.borderpad', - 'legend.labelsep' : 'legend.labelspacing', - 'legend.handlelen' : 'legend.handlelength', - 'legend.handletextsep' : 'legend.handletextpad', - 'legend.axespad' : 'legend.borderaxespad', } diff --git a/lib/matplotlib/axes.py b/lib/matplotlib/axes.py index f294186c0c5d..f74e164ae894 100644 --- a/lib/matplotlib/axes.py +++ b/lib/matplotlib/axes.py @@ -1366,11 +1366,6 @@ def axis(self, *v, **kwargs): return v - def get_frame(self): - """Return the axes Rectangle frame""" - warnings.warn('use ax.patch instead', mplDeprecation) - return self.patch - def get_legend(self): """ Return the legend.Legend instance, or None if no legend is defined @@ -8209,11 +8204,6 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, raise ValueError( "orientation kwarg %s is not recognized" % orientation) - if kwargs.get('width') is not None: - raise mplDeprecation( - 'hist now uses the rwidth to give relative width ' - 'and not absolute width') - if histtype == 'barstacked' and not stacked: stacked = True diff --git a/lib/matplotlib/backends/backend_qt.py b/lib/matplotlib/backends/backend_qt.py deleted file mode 100644 index dda129dfbd1c..000000000000 --- a/lib/matplotlib/backends/backend_qt.py +++ /dev/null @@ -1,498 +0,0 @@ -from __future__ import division, print_function -import math -import os -import sys -import warnings - -from matplotlib.cbook import mplDeprecation - -warnings.warn("QT3-based backends are deprecated and will be removed after" - " the v1.2.x release. Use the equivalent QT4 backend instead.", - mplDeprecation) - -import matplotlib -from matplotlib import verbose -from matplotlib.cbook import is_string_like, onetrue -from matplotlib.backend_bases import RendererBase, GraphicsContextBase, \ - FigureManagerBase, FigureCanvasBase, NavigationToolbar2, cursors -from matplotlib.backend_bases import ShowBase - -from matplotlib._pylab_helpers import Gcf -from matplotlib.figure import Figure -from matplotlib.mathtext import MathTextParser -from matplotlib.widgets import SubplotTool - -try: - import qt -except ImportError: - raise ImportError("Qt backend requires pyqt to be installed." - " NOTE: QT3-based backends will not work in" - " Python 3.") - -backend_version = "0.9.1" -def fn_name(): return sys._getframe(1).f_code.co_name - -DEBUG = False - -cursord = { - cursors.MOVE : qt.Qt.PointingHandCursor, - cursors.HAND : qt.Qt.WaitCursor, - cursors.POINTER : qt.Qt.ArrowCursor, - cursors.SELECT_REGION : qt.Qt.CrossCursor, - } - -def draw_if_interactive(): - """ - Is called after every pylab drawing command - """ - if matplotlib.is_interactive(): - figManager = Gcf.get_active() - if figManager != None: - figManager.canvas.draw() - -def _create_qApp(): - """ - Only one qApp can exist at a time, so check before creating one - """ - if qt.QApplication.startingUp(): - if DEBUG: print("Starting up QApplication") - global qApp - qApp = qt.QApplication( [" "] ) - qt.QObject.connect( qApp, qt.SIGNAL( "lastWindowClosed()" ), - qApp, qt.SLOT( "quit()" ) ) - #remember that matplotlib created the qApp - will be used by show() - _create_qApp.qAppCreatedHere = True - -_create_qApp.qAppCreatedHere = False - -class Show(ShowBase): - def mainloop(self): - if _create_qApp.qAppCreatedHere: - qt.qApp.exec_loop() - -show = Show() - - -def new_figure_manager( num, *args, **kwargs ): - """ - Create a new figure manager instance - """ - FigureClass = kwargs.pop('FigureClass', Figure) - thisFig = FigureClass(*args, **kwargs) - return new_figure_manager_given_figure(num, thisFig) - - -def new_figure_manager_given_figure(num, figure): - """ - Create a new figure manager instance for the given figure. - """ - canvas = FigureCanvasQT(figure) - manager = FigureManagerQT(canvas, num) - return manager - - -class FigureCanvasQT( qt.QWidget, FigureCanvasBase ): - keyvald = { qt.Qt.Key_Control : 'control', - qt.Qt.Key_Shift : 'shift', - qt.Qt.Key_Alt : 'alt', - } - # left 1, middle 2, right 3 - buttond = {1:1, 2:3, 4:2} - def __init__( self, figure ): - if DEBUG: print('FigureCanvasQt: ', figure) - _create_qApp() - - qt.QWidget.__init__( self, None, "QWidget figure" ) - FigureCanvasBase.__init__( self, figure ) - self.figure = figure - self.setMouseTracking( True ) - - w,h = self.get_width_height() - self.resize( w, h ) - - def enterEvent(self, event): - FigureCanvasBase.enter_notify_event(self, event) - - def leaveEvent(self, event): - FigureCanvasBase.leave_notify_event(self, event) - - def mousePressEvent( self, event ): - x = event.pos().x() - # flipy so y=0 is bottom of canvas - y = self.figure.bbox.height - event.pos().y() - button = self.buttond[event.button()] - FigureCanvasBase.button_press_event( self, x, y, button ) - if DEBUG: print('button pressed:', event.button()) - - def mouseDoubleClickEvent( self, event ): - x = event.pos().x() - # flipy so y=0 is bottom of canvas - y = self.figure.bbox.height - event.pos().y() - button = self.buttond[event.button()] - FigureCanvasBase.button_press_event( self, x, y, button, dblclick=True ) - if DEBUG: print('button doubleclicked:', event.button()) - - def mouseMoveEvent( self, event ): - x = event.x() - # flipy so y=0 is bottom of canvas - y = self.figure.bbox.height - event.y() - FigureCanvasBase.motion_notify_event( self, x, y ) - if DEBUG: print('mouse move') - - def mouseReleaseEvent( self, event ): - x = event.x() - # flipy so y=0 is bottom of canvas - y = self.figure.bbox.height - event.y() - button = self.buttond[event.button()] - FigureCanvasBase.button_release_event( self, x, y, button ) - if DEBUG: print('button released') - - def keyPressEvent( self, event ): - key = self._get_key( event ) - FigureCanvasBase.key_press_event( self, key ) - if DEBUG: print('key press', key) - - def keyReleaseEvent( self, event ): - key = self._get_key(event) - FigureCanvasBase.key_release_event( self, key ) - if DEBUG: print('key release', key) - - def resizeEvent( self, event ): - if DEBUG: print('resize (%d x %d)' % (event.size().width(), event.size().height())) - qt.QWidget.resizeEvent( self, event ) - w = event.size().width() - h = event.size().height() - if DEBUG: print("FigureCanvasQt.resizeEvent(", w, ",", h, ")") - dpival = self.figure.dpi - winch = w/dpival - hinch = h/dpival - self.figure.set_size_inches( winch, hinch ) - self.draw() - - def resize( self, w, h ): - # Pass through to Qt to resize the widget. - qt.QWidget.resize( self, w, h ) - - # Resize the figure by converting pixels to inches. - pixelPerInch = self.figure.dpi - wInch = w / pixelPerInch - hInch = h / pixelPerInch - self.figure.set_size_inches( wInch, hInch ) - - # Redraw everything. - self.draw() - - def sizeHint( self ): - w, h = self.get_width_height() - return qt.QSize( w, h ) - - def minumumSizeHint( self ): - return qt.QSize( 10, 10 ) - - def _get_key( self, event ): - if event.key() < 256: - key = event.text().latin1() - elif event.key() in self.keyvald: - key = self.keyvald[ event.key() ] - else: - key = None - - # TODO: Handle ctrl, alt, super modifiers. qt4 backend has implemented. - - return key - - def flush_events(self): - qt.qApp.processEvents() - - 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__ - -class FigureManagerQT( FigureManagerBase ): - """ - Public attributes - - canvas : The FigureCanvas instance - num : The Figure number - toolbar : The qt.QToolBar - window : The qt.QMainWindow - """ - - def __init__( self, canvas, num ): - if DEBUG: print('FigureManagerQT.%s' % fn_name()) - FigureManagerBase.__init__( self, canvas, num ) - self.canvas = canvas - self.window = qt.QMainWindow( None, None, qt.Qt.WDestructiveClose ) - self.window.closeEvent = self._widgetCloseEvent - - centralWidget = qt.QWidget( self.window ) - self.canvas.reparent( centralWidget, qt.QPoint( 0, 0 ) ) - - # Give the keyboard focus to the figure instead of the manager - self.canvas.setFocusPolicy( qt.QWidget.ClickFocus ) - self.canvas.setFocus() - self.set_window_title( "Figure %d" % num ) - - self.window._destroying = False - - self.toolbar = self._get_toolbar(self.canvas, centralWidget) - - # Use a vertical layout for the plot and the toolbar. Set the - # stretch to all be in the plot so the toolbar doesn't resize. - self.layout = qt.QVBoxLayout( centralWidget ) - self.layout.addWidget( self.canvas, 1 ) - - if self.toolbar: - self.layout.addWidget( self.toolbar, 0 ) - - self.window.setCentralWidget( centralWidget ) - - # Reset the window height so the canvas will be the right - # size. This ALMOST works right. The first issue is that the - # height w/ a toolbar seems to be off by just a little bit (so - # we add 4 pixels). The second is that the total width/height - # is slightly smaller that we actually want. It seems like - # the border of the window is being included in the size but - # AFAIK there is no way to get that size. - w = self.canvas.width() - h = self.canvas.height() - if self.toolbar: - h += self.toolbar.height() + 4 - self.window.resize( w, h ) - - if matplotlib.is_interactive(): - self.window.show() - - def notify_axes_change( fig ): - # This will be called whenever the current axes is changed - if self.toolbar != None: self.toolbar.update() - self.canvas.figure.add_axobserver( notify_axes_change ) - - def _widgetclosed( self ): - if self.window._destroying: return - self.window._destroying = True - Gcf.destroy(self.num) - - def _widgetCloseEvent( self, event ): - self._widgetclosed() - qt.QWidget.closeEvent( self.window, event ) - - def _get_toolbar(self, canvas, parent): - # must be inited after the window, drawingArea and figure - # attrs are set - if matplotlib.rcParams['toolbar'] == 'classic': - print("Classic toolbar is not yet supported") - elif matplotlib.rcParams['toolbar'] == 'toolbar2': - toolbar = NavigationToolbar2QT(canvas, parent) - else: - toolbar = None - return toolbar - - def resize(self, width, height): - 'set the canvas size in pixels' - self.window.resize(width, height) - - def show(self): - self.window.show() - - def destroy( self, *args ): - if self.window._destroying: return - self.window._destroying = True - if self.toolbar: self.toolbar.destroy() - if DEBUG: print("destroy figure manager") - self.window.close(True) - - def get_window_title(self): - return str(self.window.caption()) - - def set_window_title(self, title): - self.window.setCaption(title) - -class NavigationToolbar2QT( NavigationToolbar2, qt.QWidget ): - def __init__( self, canvas, parent ): - self.canvas = canvas - self.buttons = {} - - qt.QWidget.__init__( self, parent ) - - # Layout toolbar buttons horizontally. - self.layout = qt.QHBoxLayout( self ) - self.layout.setMargin( 2 ) - - NavigationToolbar2.__init__( self, canvas ) - - def _init_toolbar( self ): - basedir = os.path.join(matplotlib.rcParams[ 'datapath' ],'images') - - for text, tooltip_text, image_file, callback in self.toolitems: - if text == None: - self.layout.addSpacing( 8 ) - continue - - fname = os.path.join(basedir, image_file + '.ppm') - image = qt.QPixmap() - image.load( fname ) - - button = qt.QPushButton( qt.QIconSet( image ), "", self ) - qt.QToolTip.add( button, tooltip_text ) - - self.buttons[ text ] = button - - # The automatic layout doesn't look that good - it's too close - # to the images so add a margin around it. - margin = 4 - button.setFixedSize( image.width()+margin, image.height()+margin ) - - qt.QObject.connect( button, qt.SIGNAL( 'clicked()' ), - getattr( self, callback ) ) - self.layout.addWidget( button ) - - self.buttons[ 'Pan' ].setToggleButton( True ) - self.buttons[ 'Zoom' ].setToggleButton( True ) - - # Add the x,y location widget at the right side of the toolbar - # The stretch factor is 1 which means any resizing of the toolbar - # will resize this label instead of the buttons. - self.locLabel = qt.QLabel( "", self ) - self.locLabel.setAlignment( qt.Qt.AlignRight | qt.Qt.AlignVCenter ) - self.locLabel.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Ignored, - qt.QSizePolicy.Ignored)) - self.layout.addWidget( self.locLabel, 1 ) - - # reference holder for subplots_adjust window - self.adj_window = None - - - def destroy( self ): - for text, tooltip_text, image_file, callback in self.toolitems: - if text is not None: - qt.QObject.disconnect( self.buttons[ text ], - qt.SIGNAL( 'clicked()' ), - getattr( self, callback ) ) - - def pan( self, *args ): - self.buttons[ 'Zoom' ].setOn( False ) - NavigationToolbar2.pan( self, *args ) - - def zoom( self, *args ): - self.buttons[ 'Pan' ].setOn( False ) - NavigationToolbar2.zoom( self, *args ) - - def dynamic_update( self ): - self.canvas.draw() - - def set_message( self, s ): - self.locLabel.setText( s ) - - def set_cursor( self, cursor ): - if DEBUG: print('Set cursor' , cursor) - qt.QApplication.restoreOverrideCursor() - qt.QApplication.setOverrideCursor( qt.QCursor( cursord[cursor] ) ) - - def draw_rubberband( self, event, x0, y0, x1, y1 ): - height = self.canvas.figure.bbox.height - y1 = height - y1 - y0 = height - y0 - - w = abs(x1 - x0) - h = abs(y1 - y0) - - rect = [ int(val)for val in (min(x0,x1), min(y0, y1), w, h) ] - self.canvas.drawRectangle( rect ) - - def configure_subplots(self): - self.adj_window = qt.QMainWindow(None, None, qt.Qt.WDestructiveClose) - win = self.adj_window - win.setCaption("Subplot Configuration Tool") - - toolfig = Figure(figsize=(6,3)) - toolfig.subplots_adjust(top=0.9) - w = int (toolfig.bbox.width) - h = int (toolfig.bbox.height) - - canvas = self._get_canvas(toolfig) - tool = SubplotTool(self.canvas.figure, toolfig) - centralWidget = qt.QWidget(win) - canvas.reparent(centralWidget, qt.QPoint(0, 0)) - win.setCentralWidget(centralWidget) - - layout = qt.QVBoxLayout(centralWidget) - layout.addWidget(canvas, 1) - - win.resize(w, h) - canvas.setFocus() - win.show() - - def _get_canvas(self, fig): - return FigureCanvasQT(fig) - - def save_figure(self, *args): - filetypes = self.canvas.get_supported_filetypes_grouped() - sorted_filetypes = filetypes.items() - sorted_filetypes.sort() - default_filetype = self.canvas.get_default_filetype() - - start = self.canvas.get_default_filename() - filters = [] - selectedFilter = None - for name, exts in sorted_filetypes: - exts_list = " ".join(['*.%s' % ext for ext in exts]) - filter = '%s (%s)' % (name, exts_list) - if default_filetype in exts: - selectedFilter = filter - filters.append(filter) - filters = ';;'.join(filters) - - fname = qt.QFileDialog.getSaveFileName( - start, filters, self, "Save image", "Choose a filename to save to", - selectedFilter) - if fname: - try: - self.canvas.print_figure( unicode(fname) ) - except Exception as e: - qt.QMessageBox.critical( - self, "Error saving file", str(e), - qt.QMessageBox.Ok, qt.QMessageBox.NoButton) - - def set_history_buttons( self ): - canBackward = ( self._views._pos > 0 ) - canForward = ( self._views._pos < len( self._views._elements ) - 1 ) - self.buttons[ 'Back' ].setEnabled( canBackward ) - self.buttons[ 'Forward' ].setEnabled( canForward ) - -# set icon used when windows are minimized -try: - # TODO: This is badly broken - qt.window_set_default_icon_from_file ( - os.path.join( matplotlib.rcParams['datapath'], 'images', 'matplotlib.svg' ) ) -except: - verbose.report( 'Could not load matplotlib icon: %s' % sys.exc_info()[1] ) - - -def error_msg_qt( msg, parent=None ): - if not is_string_like( msg ): - msg = ','.join( map( str,msg ) ) - - qt.QMessageBox.warning( None, "Matplotlib", msg, qt.QMessageBox.Ok ) - -def exception_handler( type, value, tb ): - """Handle uncaught exceptions - It does not catch SystemExit - """ - msg = '' - # get the filename attribute if available (for IOError) - if hasattr(value, 'filename') and value.filename != None: - msg = value.filename + ': ' - if hasattr(value, 'strerror') and value.strerror != None: - msg += value.strerror - else: - msg += str(value) - - if len( msg ) : error_msg_qt( msg ) - - -FigureManager = FigureManagerQT diff --git a/lib/matplotlib/backends/backend_qtagg.py b/lib/matplotlib/backends/backend_qtagg.py deleted file mode 100644 index 91a9cd47133e..000000000000 --- a/lib/matplotlib/backends/backend_qtagg.py +++ /dev/null @@ -1,158 +0,0 @@ -""" -Render to qt from agg -""" -from __future__ import division, print_function - -import os, sys -import matplotlib -from matplotlib import verbose -from matplotlib.figure import Figure - -from backend_agg import FigureCanvasAgg -from backend_qt import qt, FigureManagerQT, FigureCanvasQT,\ - show, draw_if_interactive, backend_version, \ - NavigationToolbar2QT - -DEBUG = False - - -def new_figure_manager( num, *args, **kwargs ): - """ - Create a new figure manager instance - """ - if DEBUG: print('backend_qtagg.new_figure_manager') - FigureClass = kwargs.pop('FigureClass', Figure) - thisFig = FigureClass( *args, **kwargs ) - return new_figure_manager_given_figure(num, thisFig) - - -def new_figure_manager_given_figure(num, figure): - """ - Create a new figure manager instance for the given figure. - """ - canvas = FigureCanvasQTAgg(figure) - return FigureManagerQTAgg(canvas, num) - - -class NavigationToolbar2QTAgg(NavigationToolbar2QT): - def _get_canvas(self, fig): - return FigureCanvasQTAgg(fig) - -class FigureManagerQTAgg(FigureManagerQT): - def _get_toolbar(self, canvas, parent): - # must be inited after the window, drawingArea and figure - # attrs are set - if matplotlib.rcParams['toolbar']=='classic': - print("Classic toolbar is not yet supported") - elif matplotlib.rcParams['toolbar']=='toolbar2': - toolbar = NavigationToolbar2QTAgg(canvas, parent) - else: - toolbar = None - return toolbar - -class FigureCanvasQTAgg( FigureCanvasAgg, FigureCanvasQT ): - """ - The canvas the figure renders into. Calls the draw and print fig - methods, creates the renderers, etc... - - Public attribute - - figure - A Figure instance - """ - - def __init__( self, figure ): - if DEBUG: print('FigureCanvasQtAgg: ', figure) - FigureCanvasQT.__init__( self, figure ) - FigureCanvasAgg.__init__( self, figure ) - self.drawRect = False - self.rect = [] - self.replot = True - self.pixmap = qt.QPixmap() - - def resizeEvent( self, e ): - FigureCanvasQT.resizeEvent( self, e ) - - def drawRectangle( self, rect ): - self.rect = rect - self.drawRect = True - # False in repaint does not clear the image before repainting - self.repaint( False ) - - def paintEvent( self, e ): - """ - Draw to the Agg backend and then copy the image to the qt.drawable. - In Qt, all drawing should be done inside of here when a widget is - shown onscreen. - """ - - FigureCanvasQT.paintEvent( self, e ) - if DEBUG: print('FigureCanvasQtAgg.paintEvent: ', self, \ - self.get_width_height()) - - p = qt.QPainter( self ) - - # only replot data when needed - if type(self.replot) is bool: # might be a bbox for blitting - if self.replot: - FigureCanvasAgg.draw( self ) - #stringBuffer = str( self.buffer_rgba(0,0) ) - - # matplotlib is in rgba byte order. - # qImage wants to put the bytes into argb format and - # is in a 4 byte unsigned int. little endian system is LSB first - # and expects the bytes in reverse order (bgra). - if ( qt.QImage.systemByteOrder() == qt.QImage.LittleEndian ): - stringBuffer = self.renderer._renderer.tostring_bgra() - else: - stringBuffer = self.renderer._renderer.tostring_argb() - - qImage = qt.QImage( stringBuffer, self.renderer.width, - self.renderer.height, 32, None, 0, - qt.QImage.IgnoreEndian ) - - self.pixmap.convertFromImage( qImage, qt.QPixmap.Color ) - - p.drawPixmap( qt.QPoint( 0, 0 ), self.pixmap ) - - # draw the zoom rectangle to the QPainter - if ( self.drawRect ): - p.setPen( qt.QPen( qt.Qt.black, 1, qt.Qt.DotLine ) ) - p.drawRect( self.rect[0], self.rect[1], self.rect[2], self.rect[3] ) - - # we are blitting here - else: - bbox = self.replot - l, b, r, t = bbox.extents - w = int(r) - int(l) - h = int(t) - int(b) - reg = self.copy_from_bbox(bbox) - stringBuffer = reg.to_string_argb() - qImage = qt.QImage(stringBuffer, w, h, 32, None, 0, qt.QImage.IgnoreEndian) - self.pixmap.convertFromImage(qImage, qt.QPixmap.Color) - p.drawPixmap(qt.QPoint(l, self.renderer.height-t), self.pixmap) - - p.end() - self.replot = False - self.drawRect = False - - def draw( self ): - """ - Draw the figure when xwindows is ready for the update - """ - - if DEBUG: print("FigureCanvasQtAgg.draw", self) - self.replot = True - FigureCanvasAgg.draw(self) - self.repaint(False) - - def blit(self, bbox=None): - """ - Blit the region in bbox - """ - - self.replot = bbox - self.repaint(False) - - def print_figure(self, *args, **kwargs): - FigureCanvasAgg.print_figure(self, *args, **kwargs) - self.draw() diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index ed36d074ee9b..3f1af1e878e5 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -785,190 +785,6 @@ def Copy_to_Clipboard(self, event=None): wx.TheClipboard.Close() wx.TheClipboard.Flush() - def Printer_Init(self): - """ - initialize printer settings using wx methods - - Deprecated. - """ - warnings.warn("Printer* methods will be removed", mplDeprecation) - self.printerData = wx.PrintData() - self.printerData.SetPaperId(wx.PAPER_LETTER) - self.printerData.SetPrintMode(wx.PRINT_MODE_PRINTER) - self.printerPageData= wx.PageSetupDialogData() - self.printerPageData.SetMarginBottomRight((25,25)) - self.printerPageData.SetMarginTopLeft((25,25)) - self.printerPageData.SetPrintData(self.printerData) - - self.printer_width = 5.5 - self.printer_margin= 0.5 - - def _get_printerData(self): - if self._printerData is None: - warnings.warn("Printer* methods will be removed", mplDeprecation) - self._printerData = wx.PrintData() - self._printerData.SetPaperId(wx.PAPER_LETTER) - self._printerData.SetPrintMode(wx.PRINT_MODE_PRINTER) - return self._printerData - printerData = property(_get_printerData) - - def _get_printerPageData(self): - if self._printerPageData is None: - warnings.warn("Printer* methods will be removed", mplDeprecation) - self._printerPageData= wx.PageSetupDialogData() - self._printerPageData.SetMarginBottomRight((25,25)) - self._printerPageData.SetMarginTopLeft((25,25)) - self._printerPageData.SetPrintData(self.printerData) - return self._printerPageData - printerPageData = property(_get_printerPageData) - - def Printer_Setup(self, event=None): - """ - set up figure for printing. The standard wx Printer - Setup Dialog seems to die easily. Therefore, this setup - simply asks for image width and margin for printing. - Deprecated. - """ - - dmsg = """Width of output figure in inches. -The current aspect ratio will be kept.""" - - warnings.warn("Printer* methods will be removed", mplDeprecation) - dlg = wx.Dialog(self, -1, 'Page Setup for Printing' , (-1,-1)) - df = dlg.GetFont() - df.SetWeight(wx.NORMAL) - df.SetPointSize(11) - dlg.SetFont(df) - - x_wid = wx.TextCtrl(dlg,-1,value="%.2f" % self.printer_width, size=(70,-1)) - x_mrg = wx.TextCtrl(dlg,-1,value="%.2f" % self.printer_margin,size=(70,-1)) - - sizerAll = wx.BoxSizer(wx.VERTICAL) - sizerAll.Add(wx.StaticText(dlg,-1,dmsg), - 0, wx.ALL | wx.EXPAND, 5) - - sizer = wx.FlexGridSizer(0,3) - sizerAll.Add(sizer, 0, wx.ALL | wx.EXPAND, 5) - - sizer.Add(wx.StaticText(dlg,-1,'Figure Width'), - 1, wx.ALIGN_LEFT|wx.ALL, 2) - sizer.Add(x_wid, - 1, wx.ALIGN_LEFT|wx.ALL, 2) - sizer.Add(wx.StaticText(dlg,-1,'in'), - 1, wx.ALIGN_LEFT|wx.ALL, 2) - - sizer.Add(wx.StaticText(dlg,-1,'Margin'), - 1, wx.ALIGN_LEFT|wx.ALL, 2) - sizer.Add(x_mrg, - 1, wx.ALIGN_LEFT|wx.ALL, 2) - sizer.Add(wx.StaticText(dlg,-1,'in'), - 1, wx.ALIGN_LEFT|wx.ALL, 2) - - btn = wx.Button(dlg,wx.ID_OK, " OK ") - btn.SetDefault() - sizer.Add(btn, 1, wx.ALIGN_LEFT, 5) - btn = wx.Button(dlg,wx.ID_CANCEL, " CANCEL ") - sizer.Add(btn, 1, wx.ALIGN_LEFT, 5) - - dlg.SetSizer(sizerAll) - dlg.SetAutoLayout(True) - sizerAll.Fit(dlg) - - if dlg.ShowModal() == wx.ID_OK: - try: - self.printer_width = float(x_wid.GetValue()) - self.printer_margin = float(x_mrg.GetValue()) - except: - pass - - if ((self.printer_width + self.printer_margin) > 7.5): - self.printerData.SetOrientation(wx.LANDSCAPE) - else: - self.printerData.SetOrientation(wx.PORTRAIT) - dlg.Destroy() - return - - def Printer_Setup2(self, event=None): - """ - set up figure for printing. Using the standard wx Printer - Setup Dialog. - - Deprecated. - """ - - warnings.warn("Printer* methods will be removed", mplDeprecation) - if hasattr(self, 'printerData'): - data = wx.PageSetupDialogData() - data.SetPrintData(self.printerData) - else: - data = wx.PageSetupDialogData() - data.SetMarginTopLeft( (15, 15) ) - data.SetMarginBottomRight( (15, 15) ) - - dlg = wx.PageSetupDialog(self, data) - - if dlg.ShowModal() == wx.ID_OK: - data = dlg.GetPageSetupData() - tl = data.GetMarginTopLeft() - br = data.GetMarginBottomRight() - self.printerData = wx.PrintData(data.GetPrintData()) - dlg.Destroy() - - def Printer_Preview(self, event=None): - """ - generate Print Preview with wx Print mechanism - - Deprecated. - """ - warnings.warn("Printer* methods will be removed", mplDeprecation) - po1 = PrintoutWx(self, width=self.printer_width, - margin=self.printer_margin) - po2 = PrintoutWx(self, width=self.printer_width, - margin=self.printer_margin) - self.preview = wx.PrintPreview(po1,po2,self.printerData) - if not self.preview.Ok(): print("error with preview") - - self.preview.SetZoom(50) - frameInst= self - while not isinstance(frameInst, wx.Frame): - frameInst= frameInst.GetParent() - frame = wx.PreviewFrame(self.preview, frameInst, "Preview") - frame.Initialize() - frame.SetPosition(self.GetPosition()) - frame.SetSize((850,650)) - frame.Centre(wx.BOTH) - frame.Show(True) - self.gui_repaint() - - def Printer_Print(self, event=None): - """ - Print figure using wx Print mechanism - - Deprecated. - """ - warnings.warn("Printer* methods will be removed", mplDeprecation) - pdd = wx.PrintDialogData() - # SetPrintData for 2.4 combatibility - pdd.SetPrintData(self.printerData) - pdd.SetToPage(1) - printer = wx.Printer(pdd) - printout = PrintoutWx(self, width=int(self.printer_width), - margin=int(self.printer_margin)) - print_ok = printer.Print(self, printout, True) - - if wx.VERSION_STRING >= '2.5': - if not print_ok and not printer.GetLastError() == wx.PRINTER_CANCELLED: - wx.MessageBox("""There was a problem printing. - Perhaps your current printer is not set correctly?""", - "Printing", wx.OK) - else: - if not print_ok: - wx.MessageBox("""There was a problem printing. - Perhaps your current printer is not set correctly?""", - "Printing", wx.OK) - printout.Destroy() - self.gui_repaint() - def draw_idle(self): """ Delay rendering until the GUI is idle. @@ -1166,8 +982,8 @@ def _print_image(self, filename, filetype, *args, **kwargs): gc = renderer.new_gc() self.figure.draw(renderer) - - # image is the object that we call SaveFile on. + + # image is the object that we call SaveFile on. image = self.bitmap # set the JPEG quality appropriately. Unfortunately, it is only possible # to set the quality on a wx.Image object. So if we are saving a JPEG, @@ -1176,7 +992,7 @@ def _print_image(self, filename, filetype, *args, **kwargs): jpeg_quality = kwargs.get('quality',rcParams['savefig.jpeg_quality']) image = self.bitmap.ConvertToImage() image.SetOption(wx.IMAGE_OPTION_QUALITY,str(jpeg_quality)) - + # Now that we have rendered into the bitmap, save it # to the appropriate file type and clean up if is_string_like(filename): diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index 25a66dda686a..5388e40ea74b 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -31,7 +31,6 @@ from matplotlib.offsetbox import DraggableOffsetBox from matplotlib.container import ErrorbarContainer, BarContainer, StemContainer -from matplotlib.cbook import mplDeprecation import legend_handler @@ -140,13 +139,6 @@ def __init__(self, parent, handles, labels, prop=None, # properties for the legend texts fontsize=None, # keyword to set font size directly - # the following dimensions are in axes coords - pad=None, # deprecated; use borderpad - labelsep=None, # deprecated; use labelspacing - handlelen=None, # deprecated; use handlelength - handletextsep=None, # deprecated; use handletextpad - axespad=None, # deprecated; use borderaxespad - # spacing & pad defined as a fraction of the font-size borderpad=None, # the whitespace inside the legend border labelspacing=None, # the vertical space between the legend @@ -264,13 +256,6 @@ def __init__(self, parent, handles, labels, value = localdict[name] setattr(self, name, value) - # Take care the deprecated keywords - deprecated_kwds = {"pad": "borderpad", - "labelsep": "labelspacing", - "handlelen": "handlelength", - "handletextsep": "handletextpad", - "axespad": "borderaxespad"} - # convert values of deprecated keywords (ginve in axes coords) # to new vaules in a fraction of the font size @@ -278,16 +263,8 @@ def __init__(self, parent, handles, labels, bbox = parent.bbox axessize_fontsize = min(bbox.width, bbox.height) / self._fontsize - for k, v in deprecated_kwds.iteritems(): - # use deprecated value if not None and if their newer - # counter part is None. - if localdict[k] is not None and localdict[v] is None: - warnings.warn("Use '%s' instead of '%s'." % (v, k), - mplDeprecation) - setattr(self, v, localdict[k] * axessize_fontsize) - continue - - # Otherwise, use new keywords + for v in ['borderpad', 'labelspacing', 'handlelength', + 'handletextpad', 'borderaxespad']: if localdict[v] is None: setattr(self, v, rcParams["legend." + v]) else: diff --git a/lib/matplotlib/mlab.py b/lib/matplotlib/mlab.py index ff416c86c873..2c0426d78f2f 100644 --- a/lib/matplotlib/mlab.py +++ b/lib/matplotlib/mlab.py @@ -148,7 +148,6 @@ import numpy as np ma = np.ma from matplotlib import verbose -from matplotlib.cbook import mplDeprecation import matplotlib.cbook as cbook from matplotlib import docstring @@ -1182,37 +1181,6 @@ def fftsurr(x, detrend=detrend_none, window=window_none): return np.fft.ifft(z).real -def liaupunov(x, fprime): - """ - *x* is a very long trajectory from a map, and *fprime* returns the - derivative of *x*. - - This function will be removed from matplotlib. - - Returns : - .. math:: - - \lambda = \\frac{1}{n}\\sum \\ln|f^'(x_i)| - - .. seealso:: - - Lyapunov Exponent - Sec 10.5 Strogatz (1994) "Nonlinear Dynamics and Chaos". - `Wikipedia article on Lyapunov Exponent - `_. - - .. note:: - What the function here calculates may not be what you really want; - *caveat emptor*. - - It also seems that this function's name is badly misspelled. - """ - - warnings.warn("This does not belong in matplotlib and will be removed", - mplDeprecation) # 2009/06/13 - - return np.mean(np.log(np.absolute(fprime(x)))) - class FIFOBuffer: """ A FIFO queue to hold incoming *x*, *y* data in a rotating buffer @@ -1318,154 +1286,6 @@ def movavg(x,n): w[:] = 1.0/n return np.convolve(x, w, mode='valid') -def save(fname, X, fmt='%.18e',delimiter=' '): - """ - Save the data in *X* to file *fname* using *fmt* string to convert the - data to strings. - - Deprecated. Use numpy.savetxt. - - *fname* can be a filename or a file handle. If the filename ends - in '.gz', the file is automatically saved in compressed gzip - format. The :func:`load` function understands gzipped files - transparently. - - Example usage:: - - save('test.out', X) # X is an array - save('test1.out', (x,y,z)) # x,y,z equal sized 1D arrays - save('test2.out', x) # x is 1D - save('test3.out', x, fmt='%1.4e') # use exponential notation - - *delimiter* is used to separate the fields, e.g., *delimiter* ',' - for comma-separated values. - """ - - warnings.warn("use numpy.savetxt", mplDeprecation) # 2009/06/13 - - if cbook.is_string_like(fname): - if fname.endswith('.gz'): - import gzip - fh = gzip.open(fname,'wb') - else: - fh = open(fname,'w') - elif hasattr(fname, 'seek'): - fh = fname - else: - raise ValueError('fname must be a string or file handle') - - - X = np.asarray(X) - origShape = None - if X.ndim == 1: - origShape = X.shape - X.shape = len(X), 1 - for row in X: - fh.write(delimiter.join([fmt%val for val in row]) + '\n') - - if origShape is not None: - X.shape = origShape - - - - -def load(fname,comments='#',delimiter=None, converters=None,skiprows=0, - usecols=None, unpack=False, dtype=np.float_): - """ - Load ASCII data from *fname* into an array and return the array. - - Deprecated: use numpy.loadtxt. - - The data must be regular, same number of values in every row - - *fname* can be a filename or a file handle. Support for gzipped - files is automatic, if the filename ends in '.gz'. - - matfile data is not supported; for that, use :mod:`scipy.io.mio` - module. - - Example usage:: - - X = load('test.dat') # data in two columns - t = X[:,0] - y = X[:,1] - - Alternatively, you can do the same with "unpack"; see below:: - - X = load('test.dat') # a matrix of data - x = load('test.dat') # a single column of data - - - *comments*: the character used to indicate the start of a comment - in the file - - - *delimiter* is a string-like character used to seperate values - in the file. If *delimiter* is unspecified or *None*, any - whitespace string is a separator. - - - *converters*, if not *None*, is a dictionary mapping column number to - a function that will convert that column to a float (or the optional - *dtype* if specified). e.g., if column 0 is a date string:: - - converters = {0:datestr2num} - - - *skiprows* is the number of rows from the top to skip. - - - *usecols*, if not *None*, is a sequence of integer column indexes to - extract where 0 is the first column, eg ``usecols=[1,4,5]`` to extract - just the 2nd, 5th and 6th columns - - - *unpack*, if *True*, will transpose the matrix allowing you to unpack - into named arguments on the left hand side:: - - t,y = load('test.dat', unpack=True) # for two column data - x,y,z = load('somefile.dat', usecols=[3,5,7], unpack=True) - - - *dtype*: the array will have this dtype. default: ``numpy.float_`` - - .. seealso:: - - See :file:`examples/pylab_examples/load_converter.py` in the source tree - Exercises many of these options. - """ - - warnings.warn("use numpy.loadtxt", mplDeprecation) # 2009/06/13 - - if converters is None: converters = {} - fh = cbook.to_filehandle(fname) - X = [] - - if delimiter==' ': - # space splitting is a special case since x.split() is what - # you want, not x.split(' ') - def splitfunc(x): - return x.split() - else: - def splitfunc(x): - return x.split(delimiter) - - converterseq = None - for i,line in enumerate(fh): - if i 0: - version, chunk = version[:-2], version[-2:] - temp.insert(0, str(int(chunk, 16))) - return '.'.join(temp) - - def check(self): - try: - import pyqtconfig - except ImportError: - raise CheckFailed("pyqt not found") - else: - try: - qt_version = pyqtconfig.Configuration().qt_version - qt_version = self.convert_qt_version(qt_version) - except AttributeError: - qt_version = "" - - BackendAgg.force = True - - return ("Qt: %s, PyQt: %s" % - (qt_version, - pyqtconfig.Configuration().pyqt_version_str)) - - class BackendQt4(OptionalBackendPackage): name = "qt4agg" diff --git a/unit/memleak_gui.py b/unit/memleak_gui.py index 77570c86a5c2..2478bb87bb20 100755 --- a/unit/memleak_gui.py +++ b/unit/memleak_gui.py @@ -89,11 +89,6 @@ print("# PyQt4 version: %s, Qt version %x" % \ (PyQt4.pyqtconfig.Configuration().pyqt_version_str, PyQt4.pyqtconfig.Configuration().qt_version)) - elif backend.startswith("qt"): - import pyqtconfig - print("# pyqt version: %s, qt version: %x" % \ - (pyqtconfig.Configuration().pyqt_version_str, - pyqtconfig.Configuration().qt_version)) elif backend.startswith("wx"): import wx print("# wxPython version: %s" % wx.__version__) From 8364f8729e2132441cb2582ff218443acf1b0298 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Mon, 20 May 2013 14:28:32 -0400 Subject: [PATCH 03/11] Use new warn_deprecated helper function and decorator where appropriate. --- lib/matplotlib/axes.py | 14 +++-- lib/matplotlib/axis.py | 8 +-- lib/matplotlib/cbook.py | 114 +++++++++++++++++++++++++++++----------- lib/matplotlib/mpl.py | 9 ++-- 4 files changed, 95 insertions(+), 50 deletions(-) diff --git a/lib/matplotlib/axes.py b/lib/matplotlib/axes.py index f74e164ae894..da8dda0dd610 100644 --- a/lib/matplotlib/axes.py +++ b/lib/matplotlib/axes.py @@ -37,7 +37,6 @@ import matplotlib.ticker as mticker import matplotlib.transforms as mtransforms import matplotlib.tri as mtri -from matplotlib.cbook import mplDeprecation from matplotlib.container import BarContainer, ErrorbarContainer, StemContainer iterable = cbook.iterable @@ -1062,8 +1061,8 @@ def set_aspect(self, aspect, adjustable=None, anchor=None): the option 'normal' for aspect is deprecated. Use 'auto' instead. """ if aspect == 'normal': - warnings.warn("Use 'auto' instead of 'normal' for aspect. Will " - "be removed in 1.4.x", mplDeprecation) + cbook.warn_deprecated( + '1.2', name='normal', alternative='auto', obj_type='aspect') self._aspect = 'auto' elif aspect in ('equal', 'auto'): @@ -6276,9 +6275,8 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None, faceted = kwargs.pop('faceted', None) edgecolors = kwargs.get('edgecolors', None) if faceted is not None: - warnings.warn("The faceted option is deprecated. " - "Please use edgecolor instead. Will " - "be removed in 1.4", mplDeprecation) + cbook.warn_deprecated( + '1.2', 'faceted', alternative='edgecolor', obj_type='option') if faceted: edgecolors = None else: @@ -7500,8 +7498,8 @@ def pcolor(self, *args, **kwargs): vmin = kwargs.pop('vmin', None) vmax = kwargs.pop('vmax', None) if 'shading' in kwargs: - warnings.warn("Use edgecolors instead of shading. " - "Will be removed in 1.4", mplDeprecation) + cbook.warn_deprecated( + '1.2', 'shading', alternative='edgecolors', obj_type='option') shading = kwargs.pop('shading', 'flat') X, Y, C = self._pcolorargs('pcolor', *args) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 98c1835deba4..506c43baf2aa 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -18,8 +18,6 @@ import numpy as np import warnings -from cbook import mplDeprecation - GRIDLINE_INTERPOLATION_STEPS = 180 @@ -684,15 +682,11 @@ def get_transform(self): def get_scale(self): return self._scale.name + @cbook.deprecated('1.3') def set_scale(self, value, **kwargs): """ - Deprecated 1.3. - This should be a private function (moved to _set_scale) """ - warnings.warn("This function has been made private and moved" - "to `_set_scale`. This wrapper function will be " - "removed in 1.4", mplDeprecation) self._set_scale(value, **kwargs) def _set_scale(self, value, **kwargs): diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 911e606108bc..225e2d657947 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -45,6 +45,84 @@ class MatplotlibDeprecationWarning(UserWarning): mplDeprecation = MatplotlibDeprecationWarning +def _generate_deprecation_message( + since, message='', name='', alternative='', pending=False, + obj_type='attribute'): + + if not message: + altmessage = '' + + if pending: + message = ( + 'The %(func)s %(obj_type)s will be deprecated in a ' + 'future version.') + else: + message = ( + 'The %(func)s %(obj_type)s was deprecated in version ' + '%(since)s.') + if alternative: + altmessage = ' Use %s instead.' % alternative + + message = ((message % { + 'func': name, + 'name': name, + 'alternative': alternative, + 'obj_type': obj_type, + 'since': since}) + + altmessage) + + return message + + +def warn_deprecated( + since, message='', name='', alternative='', pending=False, + obj_type='attribute'): + """ + Used display deprecation warning in a standard way. + + Parameters + ------------ + since : str + The release at which this API became deprecated. This is + required. + + message : str, optional + Override the default deprecation message. The format + specifier `%(func)s` may be used for the name of the function, + and `%(alternative)s` may be used in the deprecation message + to insert the name of an alternative to the deprecated + function. `%(obj_type)` may be used to insert a friendly name + for the type of object being deprecated. + + name : str, optional + The name of the deprecated function; if not provided the name + is automatically determined from the passed in function, + though this is useful in the case of renamed functions, where + the new function is just assigned to the name of the + deprecated function. For example:: + + def new_function(): + ... + oldFunction = new_function + + alternative : str, optional + An alternative function that the user may use in place of the + deprecated function. The deprecation warning will tell the user about + this alternative if provided. + + pending : bool, optional + If True, uses a PendingDeprecationWarning instead of a + DeprecationWarning. + + obj_type : str, optional + The object type being deprecated. + """ + message = _generate_deprecation_message( + since, message, name, alternative, pending, 'function') + + warnings.warn(message, mplDeprecation, stacklevel=1) + + def deprecated(since, message='', name='', alternative='', pending=False, obj_type='function'): """ @@ -84,7 +162,6 @@ def new_function(): If True, uses a PendingDeprecationWarning instead of a DeprecationWarning. """ - def deprecate(func, message=message, name=name, alternative=alternative, pending=pending): import functools @@ -112,23 +189,8 @@ def deprecate(func, message=message, name=name, alternative=alternative, if not name: name = func.__name__ - altmessage = '' - if not message or type(message) == type(deprecate): - if pending: - message = ('The %(func)s %(obj_type)s will be deprecated in a ' - 'future version.') - else: - message = ('The %(func)s %(obj_type)s is deprecated and may ' - 'be removed in a future version.') - if alternative: - altmessage = '\n Use %s instead.' % alternative - - message = ((message % { - 'func': name, - 'name': name, - 'alternative': alternative, - 'obj_type': obj_type}) + - altmessage) + message = _generate_deprecation_message( + since, message, name, alternative, pending, 'function') @functools.wraps(func) def deprecated_func(*args, **kwargs): @@ -140,12 +202,10 @@ def deprecated_func(*args, **kwargs): if not old_doc: old_doc = '' old_doc = textwrap.dedent(old_doc).strip('\n') - altmessage = altmessage.strip() - if not altmessage: - altmessage = message.strip() + message = message.strip() new_doc = (('\n.. deprecated:: %(since)s' '\n %(message)s\n\n' % - {'since': since, 'message': altmessage.strip()}) + old_doc) + {'since': since, 'message': message}) + old_doc) if not old_doc: # This is to prevent a spurious 'unexected unindent' warning from # docutils when the original docstring was blank. @@ -157,9 +217,6 @@ def deprecated_func(*args, **kwargs): deprecated_func = classmethod(deprecated_func) return deprecated_func - if type(message) == type(deprecate): - return deprecate(message) - return deprecate @@ -405,15 +462,12 @@ class CallbackRegistry: functions). This technique was shared by Peter Parente on his `"Mindtrove" blog `_. - - .. deprecated:: 1.3.0 """ def __init__(self, *args): if len(args): - warnings.warn( + warn_deprecated('1.3', message= "CallbackRegistry no longer requires a list of callback " - "types. Ignoring arguments. *args will be removed in 1.5", - mplDeprecation) + "types. Ignoring arguments. *args will be removed in 1.5") self.callbacks = dict() self._cid = 0 self._func_cid_map = {} diff --git a/lib/matplotlib/mpl.py b/lib/matplotlib/mpl.py index 877a99da1de1..62051b86ec6b 100644 --- a/lib/matplotlib/mpl.py +++ b/lib/matplotlib/mpl.py @@ -2,14 +2,13 @@ .. note:: Deprecated in 1.3 """ import warnings -from matplotlib.cbook import mplDeprecation -warnings.warn( - "matplotlib.mpl is deprecated and will be removed in version 1.4." - "Please use `import matplotlib as mpl` instead", mplDeprecation) +from matplotlib import cbook +cbook.warn_deprecated( + '1.3', 'matplotlib.mpl', alterative='`import matplotlib as mpl`', + obj_type='module') from matplotlib import artist from matplotlib import axis from matplotlib import axes -from matplotlib import cbook from matplotlib import collections from matplotlib import colors from matplotlib import colorbar From c933a624c91c8fc6946258315ecc9ef38b68461c Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Mon, 20 May 2013 14:58:04 -0400 Subject: [PATCH 04/11] Warn about incompleteness of CocoaAgg backend --- doc/api/api_changes.rst | 5 +++++ lib/matplotlib/backends/backend_cocoaagg.py | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/api/api_changes.rst b/doc/api/api_changes.rst index 219c2a5d7553..717ac5f48448 100644 --- a/doc/api/api_changes.rst +++ b/doc/api/api_changes.rst @@ -12,6 +12,8 @@ For new features that were added to matplotlib, please see :ref:`whats-new`. +.. _changes_in_1_3: + Changes in 1.3.x ================ @@ -61,6 +63,9 @@ Changes in 1.3.x - `widgets.HorizontalSpanSelector` has been removed. Use `widgets.SpanSelector` instead. +* The CocoaAgg backend has been deprecated, with the possibility for + deletion or resurrection in a future release. + * The top-level functions in `matplotlib.path` that are implemented in C++ were never meant to be public. Instead, users should use the Pythonic wrappers for them in the `path.Path` and diff --git a/lib/matplotlib/backends/backend_cocoaagg.py b/lib/matplotlib/backends/backend_cocoaagg.py index 5c9c04938874..945f57ef7cc5 100644 --- a/lib/matplotlib/backends/backend_cocoaagg.py +++ b/lib/matplotlib/backends/backend_cocoaagg.py @@ -25,6 +25,12 @@ from AppKit import * from PyObjCTools import NibClassBuilder, AppHelper +from matplotlib import cbook +cbook.warn_deprecated( + '1.3', + message="The CocoaAgg backend is not a fully-functioning backend. " + "It may be removed in matplotlib 1.4.") + import matplotlib from matplotlib.figure import Figure from matplotlib.backend_bases import FigureManagerBase, FigureCanvasBase @@ -292,4 +298,3 @@ def WMEnable(name='Python'): print('SetFrontProcess', (err, psn), file=sys.stderr) return False return True - From e955a740f68024ee1a0b564754e4b190925549f5 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Mon, 20 May 2013 15:06:17 -0400 Subject: [PATCH 05/11] Remove the Fltk backend --- doc/faq/usage_faq.rst | 15 +- doc/glossary/index.rst | 9 - doc/users/event_handling.rst | 26 +- doc/users/screenshots.rst | 2 +- doc/users/whats_new.rst | 10 + .../old_animation/animation_blit_fltk.py | 55 -- examples/misc/rc_traits.py | 5 +- lib/matplotlib/backends/backend_fltkagg.py | 645 ------------------ lib/matplotlib/backends/windowing.py | 2 +- lib/matplotlib/rcsetup.py | 4 +- 10 files changed, 33 insertions(+), 740 deletions(-) delete mode 100644 examples/animation/old_animation/animation_blit_fltk.py delete mode 100644 lib/matplotlib/backends/backend_fltkagg.py diff --git a/doc/faq/usage_faq.rst b/doc/faq/usage_faq.rst index 98af2a3aaece..5d17b9c34e36 100644 --- a/doc/faq/usage_faq.rst +++ b/doc/faq/usage_faq.rst @@ -164,12 +164,11 @@ others in web application servers to dynamically serve up graphs. To support all of these use cases, matplotlib can target different outputs, and each of these capabilities is called a backend; the "frontend" is the user facing code, ie the plotting code, whereas the -"backend" does all the hard work behind-the-scenes to make the -figure. There are two types of backends: user interface backends (for -use in pygtk, wxpython, tkinter, qt4, macosx, or fltk; also -referred to as "interactive backends") and hardcopy backends to -make image files (PNG, SVG, PDF, PS; also referred to as "non-interactive -backends"). +"backend" does all the hard work behind-the-scenes to make the figure. +There are two types of backends: user interface backends (for use in +pygtk, wxpython, tkinter, qt4, or macosx; also referred to as +"interactive backends") and hardcopy backends to make image files +(PNG, SVG, PDF, PS; also referred to as "non-interactive backends"). There are a two primary ways to configure your backend. One is to set the ``backend`` parameter in your ``matplotlibrc`` file (see @@ -267,9 +266,6 @@ WX Native :term:`wxWidgets` drawing to a :term:`wxWidgets` Canvas (not recommended) (requires wxPython_) TkAgg Agg rendering to a :term:`Tk` canvas (requires TkInter_) Qt4Agg Agg rendering to a :term:`Qt4` canvas (requires PyQt4_) -FLTKAgg Agg rendering to a :term:`FLTK` canvas (requires pyFLTK_) - (not widely used; consider TKAgg, GTKAgg, WXAgg, or - QT4Agg instead) macosx Cocoa rendering in OSX windows (presently lacks blocking show() behavior when matplotlib is in non-interactive mode) @@ -287,7 +283,6 @@ macosx Cocoa rendering in OSX windows .. _wxPython: http://www.wxpython.org/ .. _TkInter: http://wiki.python.org/moin/TkInter .. _PyQt4: http://www.riverbankcomputing.co.uk/software/pyqt/intro -.. _pyFLTK: http://pyfltk.sourceforge.net diff --git a/doc/glossary/index.rst b/doc/glossary/index.rst index 0332a7f7b41b..de17328cffca 100644 --- a/doc/glossary/index.rst +++ b/doc/glossary/index.rst @@ -22,10 +22,6 @@ Glossary EPS Encapsulated Postscript (`EPS `_) - FLTK - `FLTK `_ (pronounced "fulltick") is a cross-platform C++ GUI toolkit for - UNIX/Linux (X11), Microsoft Windows, and MacOS X - freetype `freetype `_ is a font rasterization library used by matplotlib which supports TrueType, Type 1, and @@ -68,11 +64,6 @@ Glossary channel. PDF was designed in part as a next-generation document format to replace postscript - pyfltk - `pyfltk `_ provides python - wrappers for the :term:`FLTK` widgets library for use with - FLTKAgg - pygtk `pygtk `_ provides python wrappers for the :term:`GTK` widgets library for use with the GTK or GTKAgg diff --git a/doc/users/event_handling.rst b/doc/users/event_handling.rst index 03a087bd1546..452e159aa0d7 100644 --- a/doc/users/event_handling.rst +++ b/doc/users/event_handling.rst @@ -4,19 +4,19 @@ Event handling and picking ************************** -matplotlib works with 6 user interface toolkits (wxpython, tkinter, -qt, gtk, fltk and macosx) and in order to support features like interactive -panning and zooming of figures, it is helpful to the developers to -have an API for interacting with the figure via key presses and mouse -movements that is "GUI neutral" so we don't have to repeat a lot of -code across the different user interfaces. Although the event -handling API is GUI neutral, it is based on the GTK model, which was -the first user interface matplotlib supported. The events that are -triggered are also a bit richer vis-a-vis matplotlib than standard GUI -events, including information like which :class:`matplotlib.axes.Axes` -the event occurred in. The events also understand the matplotlib -coordinate system, and report event locations in both pixel and data -coordinates. +matplotlib works with a number of user interface toolkits (wxpython, +tkinter, qt4, gtk, and macosx) and in order to support features like +interactive panning and zooming of figures, it is helpful to the +developers to have an API for interacting with the figure via key +presses and mouse movements that is "GUI neutral" so we don't have to +repeat a lot of code across the different user interfaces. Although +the event handling API is GUI neutral, it is based on the GTK model, +which was the first user interface matplotlib supported. The events +that are triggered are also a bit richer vis-a-vis matplotlib than +standard GUI events, including information like which +:class:`matplotlib.axes.Axes` the event occurred in. The events also +understand the matplotlib coordinate system, and report event +locations in both pixel and data coordinates. .. _event-connections: diff --git a/doc/users/screenshots.rst b/doc/users/screenshots.rst index ba260701a0b0..fff882487e99 100644 --- a/doc/users/screenshots.rst +++ b/doc/users/screenshots.rst @@ -265,7 +265,7 @@ rendering of strings with the *usetex* option. EEG demo ========= -You can embed matplotlib into pygtk, wxpython, Tk, FLTK or Qt4 +You can embed matplotlib into pygtk, wxpython, Tk, or Qt4 applications. Here is a screenshot of an eeg viewer called pbrain which is part of the NeuroImaging in Python suite `NIPY `_. Pbrain is written in pygtk using diff --git a/doc/users/whats_new.rst b/doc/users/whats_new.rst index 1cba586999f1..fc3b5769a4f6 100644 --- a/doc/users/whats_new.rst +++ b/doc/users/whats_new.rst @@ -21,6 +21,16 @@ revision, see the :ref:`github-stats`. new in matplotlib-1.3 ===================== + +Housecleaning +------------- + +A number of features that were deprecated in 1.2 or earlier, or have +not been in a working state for a long time have been removed. +Highlights in include removing the Qt version 3 backends, and the +FltkAgg and Emf backends. See :ref:`changes_in_1_3` for a complete +list. + `xkcd`-style sketch plotting ---------------------------- diff --git a/examples/animation/old_animation/animation_blit_fltk.py b/examples/animation/old_animation/animation_blit_fltk.py deleted file mode 100644 index 9a178bc540ff..000000000000 --- a/examples/animation/old_animation/animation_blit_fltk.py +++ /dev/null @@ -1,55 +0,0 @@ -from __future__ import print_function -import sys -import fltk -import matplotlib -matplotlib.use('FltkAgg') -import pylab as p -import numpy as npy -import time - - -# save the clean slate background -- everything but the animated line -# is drawn and saved in the pixel buffer background -class animator: - def __init__(self,ax): - self.ax=ax - self.canvas=ax.figure.canvas - self.canvas.mpl_connect('draw_event',self.clear) - self.cnt=0 - self.background=None - - # for profiling - self.tstart = time.time() - - def clear(self,event): - self.background = self.canvas.copy_from_bbox(self.ax.bbox) - - def update(self,ptr): - # restore the clean slate background - if self.background is None: - self.background = self.canvas.copy_from_bbox(self.ax.bbox) - self.canvas.restore_region(self.background) - # update the data - line.set_ydata(npy.sin(x+self.cnt/10.0)) - # just draw the animated artist - self.ax.draw_artist(line) - # just redraw the axes rectangle - self.canvas.blit(ax.bbox) - self.cnt+=1 - if self.cnt==1000: - # print the timing info and quit - print('FPS:' , 1000/(time.time()-self.tstart)) - sys.exit() - return True - -ax = p.subplot(111) -p.subplots_adjust(left=0.3, bottom=0.3) # check for flipy bugs -p.grid() # to ensure proper background restore -# create the initial line -x = npy.arange(0,2*npy.pi,0.01) -line, = p.plot(x, npy.sin(x), animated=True) -p.draw() -anim=animator(ax) - -fltk.Fl.add_idle(anim.update) -fltk.Fl.run() diff --git a/examples/misc/rc_traits.py b/examples/misc/rc_traits.py index ffad4701a282..ee74f750f8b5 100644 --- a/examples/misc/rc_traits.py +++ b/examples/misc/rc_traits.py @@ -127,7 +127,7 @@ class PatchRC(traits.HasTraits): antialiased = flexible_true_trait timezones = 'UTC', 'US/Central', 'ES/Eastern' # fixme: and many more -backends = ('GTKAgg', 'Cairo', 'FltkAgg', 'GDK', 'GTK', 'Agg', +backends = ('GTKAgg', 'Cairo', 'GDK', 'GTK', 'Agg', 'GTKCairo', 'PS', 'SVG', 'Template', 'TkAgg', 'WX') @@ -192,6 +192,3 @@ def __init__(self, print() print('Patch') p.print_traits() - - - diff --git a/lib/matplotlib/backends/backend_fltkagg.py b/lib/matplotlib/backends/backend_fltkagg.py deleted file mode 100644 index 52d4bd3b1efd..000000000000 --- a/lib/matplotlib/backends/backend_fltkagg.py +++ /dev/null @@ -1,645 +0,0 @@ -""" -A backend for FLTK - -Copyright: Gregory Lielens, Free Field Technologies SA and - John D. Hunter 2004 - -This code is released under the matplotlib license - -""" - -from __future__ import division, print_function - -import os, sys, math - -import fltk as Fltk -from backend_agg import FigureCanvasAgg - -import os.path - -import matplotlib - -from matplotlib import rcParams, verbose -from matplotlib.cbook import is_string_like -from matplotlib.backend_bases import \ - RendererBase, GraphicsContextBase, FigureManagerBase, FigureCanvasBase,\ - NavigationToolbar2, cursors -from matplotlib.backend_bases import ShowBase - - -from matplotlib.figure import Figure -from matplotlib._pylab_helpers import Gcf -import matplotlib.backends.windowing as windowing -from matplotlib.widgets import SubplotTool - -# the true dots per inch on the screen; should be display dependent -# see http://groups.google.com/groups?q=screen+dpi+x11&hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=off&selm=7077.26e81ad5%40swift.cs.tcd.ie&rnum=5 for some info about screen dpi -PIXELS_PER_INCH = 75 - -cursord= { - cursors.HAND: Fltk.FL_CURSOR_HAND, - cursors.POINTER: Fltk.FL_CURSOR_ARROW, - cursors.SELECT_REGION: Fltk.FL_CURSOR_CROSS, - cursors.MOVE: Fltk.FL_CURSOR_MOVE - } - -special_key={ - Fltk.FL_Shift_R:'shift', - Fltk.FL_Shift_L:'shift', - Fltk.FL_Control_R:'control', - Fltk.FL_Control_L:'control', - Fltk.FL_Control_R:'control', - Fltk.FL_Control_L:'control', - 65515:'win', - 65516:'win', - } - - -def error_msg_fltk(msg, parent=None): - Fltk.fl_message(msg) - - -def draw_if_interactive(): - if matplotlib.is_interactive(): - figManager = Gcf.get_active() - if figManager is not None: - figManager.canvas.draw() - -class Show(ShowBase): - def mainloop(self): - Fltk.Fl.run() - -show = Show() - - -def new_figure_manager(num, *args, **kwargs): - """ - Create a new figure manager instance - """ - FigureClass = kwargs.pop('FigureClass', Figure) - figure = FigureClass(*args, **kwargs) - return new_figure_manager_given_figure(num, figure) - - -def new_figure_manager_given_figure(num, figure): - """ - Create a new figure manager instance for the given figure. - """ - window = Fltk.Fl_Double_Window(10,10,30,30) - canvas = FigureCanvasFltkAgg(figure) - window.end() - #Fltk.Fl.visual(Fltk.FL_DOUBLE) - window.show() - window.make_current() - figManager = FigureManagerFltkAgg(canvas, num, window) - if matplotlib.is_interactive(): - figManager.show() - return figManager - - -class FltkCanvas(Fltk.Fl_Widget): - - def __init__(self,x,y,w,h,l,source): - Fltk.Fl_Widget.__init__(self, 0, 0, w, h, "canvas") - self._source=source - self._oldsize=(None,None) - self._draw_overlay = False - self._button = None - self._key = None - - - def draw(self): - newsize=(self.w(),self.h()) - if(self._oldsize !=newsize): - self._oldsize =newsize - self._source.resize(newsize) - self._source.draw() - t1,t2,w,h = self._source.figure.bbox.bounds - Fltk.fl_draw_image(self._source.buffer_rgba(),0,0,int(w),int(h),4,0) - self.redraw() - - def blit(self,bbox=None): - if bbox is None: - t1,t2,w,h = self._source.figure.bbox.bounds - else: - t1o,t2o,wo,ho = self._source.figure.bbox.bounds - t1,t2,w,h = bbox.bounds - x,y=int(t1),int(t2) - Fltk.fl_draw_image(self._source.buffer_rgba(),x,y,int(w),int(h),4,int(wo)*4) - #self.redraw() - - def handle(self, event): - x=Fltk.Fl.event_x() - y=Fltk.Fl.event_y() - yf=self._source.figure.bbox.height - y - if event == Fltk.FL_FOCUS or event == Fltk.FL_UNFOCUS: - return 1 - elif event == Fltk.FL_KEYDOWN: - ikey= Fltk.Fl.event_key() - if(ikey<=255): - self._key=chr(ikey) - else: - try: - self._key=special_key[ikey] - except: - self._key=None - - # TODO: Handle ctrl, alt, super modifiers. - FigureCanvasBase.key_press_event(self._source, self._key) - return 1 - elif event == Fltk.FL_KEYUP: - FigureCanvasBase.key_release_event(self._source, self._key) - self._key=None - elif event == Fltk.FL_PUSH: - self.window().make_current() - if Fltk.Fl.event_button1(): - self._button = 1 - elif Fltk.Fl.event_button2(): - self._button = 2 - elif Fltk.Fl.event_button3(): - self._button = 3 - else: - self._button = None - - if self._draw_overlay: - self._oldx=x - self._oldy=y - if Fltk.Fl.event_clicks(): # according to docs, event_clicks() returns nonzero if this is a double click. - FigureCanvasBase.button_press_event(self._source, x, yf, self._button, dblclick=True) - return 1 - else: - FigureCanvasBase.button_press_event(self._source, x, yf, self._button) - return 1 - elif event == Fltk.FL_ENTER: - self.take_focus() - return 1 - elif event == Fltk.FL_LEAVE: - return 1 - elif event == Fltk.FL_MOVE: - FigureCanvasBase.motion_notify_event(self._source, x, yf) - return 1 - elif event == Fltk.FL_DRAG: - self.window().make_current() - if self._draw_overlay: - self._dx=Fltk.Fl.event_x()-self._oldx - self._dy=Fltk.Fl.event_y()-self._oldy - Fltk.fl_overlay_rect(self._oldx,self._oldy,self._dx,self._dy) - FigureCanvasBase.motion_notify_event(self._source, x, yf) - return 1 - elif event == Fltk.FL_RELEASE: - self.window().make_current() - if self._draw_overlay: - Fltk.fl_overlay_clear() - FigureCanvasBase.button_release_event(self._source, x, yf, self._button) - self._button = None - return 1 - return 0 - -class FigureCanvasFltkAgg(FigureCanvasAgg): - def __init__(self, figure): - FigureCanvasAgg.__init__(self,figure) - t1,t2,w,h = self.figure.bbox.bounds - w, h = int(w), int(h) - self.canvas=FltkCanvas(0, 0, w, h, "canvas",self) - #self.draw() - - def resize(self,size): - w, h = size - # compute desired figure size in inches - dpival = self.figure.dpi - winch = w/dpival - hinch = h/dpival - self.figure.set_size_inches(winch,hinch) - - def draw(self): - FigureCanvasAgg.draw(self) - self.canvas.redraw() - - def blit(self,bbox): - self.canvas.blit(bbox) - - show = draw - - def widget(self): - return self.canvas - - 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__ - -def destroy_figure(ptr, figman): - figman.window.hide() - Fltk.Fl.wait(0) # This is needed to make the last figure vanish. - Gcf.destroy(figman._num) - -class FigureManagerFltkAgg(FigureManagerBase): - """ - Public attributes - - canvas : The FigureCanvas instance - num : The Figure number - toolbar : The fltk.Toolbar - window : The fltk.Window - """ - def __init__(self, canvas, num, window): - FigureManagerBase.__init__(self, canvas, num) - #Fltk container window - t1,t2,w,h = canvas.figure.bbox.bounds - w, h = int(w), int(h) - self.window = window - self.window.size(w,h+30) - self.window_title="Figure %d" % num - self.window.label(self.window_title) - self.window.size_range(350,200) - self.window.callback(destroy_figure,self) - self.canvas = canvas - self._num = num - if matplotlib.rcParams['toolbar']=='classic': - self.toolbar = NavigationToolbar( canvas, self ) - elif matplotlib.rcParams['toolbar']=='toolbar2': - self.toolbar = NavigationToolbar2FltkAgg( canvas, self ) - else: - self.toolbar = None - self.window.add_resizable(canvas.widget()) - if self.toolbar: - self.window.add(self.toolbar.widget()) - self.toolbar.update() - self.window.show() - - def notify_axes_change(fig): - 'this will be called whenever the current axes is changed' - if self.toolbar != None: self.toolbar.update() - self.canvas.figure.add_axobserver(notify_axes_change) - - def resize(self, event): - width, height = event.width, event.height - self.toolbar.configure(width=width) # , height=height) - - def show(self): - _focus = windowing.FocusManager() - self.canvas.draw() - self.window.redraw() - - def destroy(self): - self.window.hide() - Fltk.Fl.wait(0) # This is needed to make the last figure vanish. - Gcf.destroy(self._num) - - def set_window_title(self, title): - self.window_title=title - self.window.label(title) - -class AxisMenu: - def __init__(self, toolbar): - self.toolbar=toolbar - self._naxes = toolbar.naxes - self._mbutton = Fltk.Fl_Menu_Button(0,0,50,10,"Axes") - self._mbutton.add("Select All",0,select_all,self,0) - self._mbutton.add("Invert All",0,invert_all,self,Fltk.FL_MENU_DIVIDER) - self._axis_txt=[] - self._axis_var=[] - for i in range(self._naxes): - self._axis_txt.append("Axis %d" % (i+1)) - self._mbutton.add(self._axis_txt[i],0,set_active,self,Fltk.FL_MENU_TOGGLE) - for i in range(self._naxes): - self._axis_var.append(self._mbutton.find_item(self._axis_txt[i])) - self._axis_var[i].set() - def adjust(self, naxes): - if self._naxes < naxes: - for i in range(self._naxes, naxes): - self._axis_txt.append("Axis %d" % (i+1)) - self._mbutton.add(self._axis_txt[i],0,set_active,self,Fltk.FL_MENU_TOGGLE) - for i in range(self._naxes, naxes): - self._axis_var.append(self._mbutton.find_item(self._axis_txt[i])) - self._axis_var[i].set() - elif self._naxes > naxes: - for i in range(self._naxes-1, naxes-1, -1): - self._mbutton.remove(i+2) - if(naxes): - self._axis_var=self._axis_var[:naxes-1] - self._axis_txt=self._axis_txt[:naxes-1] - else: - self._axis_var=[] - self._axis_txt=[] - self._naxes = naxes - set_active(0,self) - - def widget(self): - return self._mbutton - - def get_indices(self): - a = [i for i in range(len(self._axis_var)) if self._axis_var[i].value()] - return a - -def set_active(ptr,amenu): - amenu.toolbar.set_active(amenu.get_indices()) - -def invert_all(ptr,amenu): - for a in amenu._axis_var: - if not a.value(): a.set() - set_active(ptr,amenu) - -def select_all(ptr,amenu): - for a in amenu._axis_var: - a.set() - set_active(ptr,amenu) - -class FLTKButton: - def __init__(self, text, file, command,argument,type="classic"): - file = os.path.join(rcParams['datapath'], 'images', file) - self.im = Fltk.Fl_PNM_Image(file) - size=26 - if type=="repeat": - self.b = Fltk.Fl_Repeat_Button(0,0,size,10) - self.b.box(Fltk.FL_THIN_UP_BOX) - elif type=="classic": - self.b = Fltk.Fl_Button(0,0,size,10) - self.b.box(Fltk.FL_THIN_UP_BOX) - elif type=="light": - self.b = Fltk.Fl_Light_Button(0,0,size+20,10) - self.b.box(Fltk.FL_THIN_UP_BOX) - elif type=="pushed": - self.b = Fltk.Fl_Button(0,0,size,10) - self.b.box(Fltk.FL_UP_BOX) - self.b.down_box(Fltk.FL_DOWN_BOX) - self.b.type(Fltk.FL_TOGGLE_BUTTON) - self.tooltiptext=text+" " - self.b.tooltip(self.tooltiptext) - self.b.callback(command,argument) - self.b.image(self.im) - self.b.deimage(self.im) - self.type=type - - def widget(self): - return self.b - -class NavigationToolbar: - """ - Public attriubutes - - canvas - the FigureCanvas (FigureCanvasFltkAgg = customised fltk.Widget) - - - """ - - def __init__(self, canvas, figman): - #xmin, xmax = canvas.figure.bbox.intervalx - #height, width = 50, xmax-xmin - self.canvas = canvas - self.figman = figman - - Fltk.Fl_File_Icon.load_system_icons() - self._fc = Fltk.Fl_File_Chooser( ".", "*", Fltk.Fl_File_Chooser.CREATE, "Save Figure" ) - self._fc.hide() - t1,t2,w,h = canvas.figure.bbox.bounds - w, h = int(w), int(h) - self._group = Fltk.Fl_Pack(0,h+2,1000,26) - self._group.type(Fltk.FL_HORIZONTAL) - self._axes=self.canvas.figure.axes - self.naxes = len(self._axes) - self.omenu = AxisMenu( toolbar=self) - - self.bLeft = FLTKButton( - text="Left", file="stock_left.ppm", - command=pan,argument=(self,1,'x'),type="repeat") - - self.bRight = FLTKButton( - text="Right", file="stock_right.ppm", - command=pan,argument=(self,-1,'x'),type="repeat") - - self.bZoomInX = FLTKButton( - text="ZoomInX",file="stock_zoom-in.ppm", - command=zoom,argument=(self,1,'x'),type="repeat") - - self.bZoomOutX = FLTKButton( - text="ZoomOutX", file="stock_zoom-out.ppm", - command=zoom, argument=(self,-1,'x'),type="repeat") - - self.bUp = FLTKButton( - text="Up", file="stock_up.ppm", - command=pan,argument=(self,1,'y'),type="repeat") - - self.bDown = FLTKButton( - text="Down", file="stock_down.ppm", - command=pan,argument=(self,-1,'y'),type="repeat") - - self.bZoomInY = FLTKButton( - text="ZoomInY", file="stock_zoom-in.ppm", - command=zoom,argument=(self,1,'y'),type="repeat") - - self.bZoomOutY = FLTKButton( - text="ZoomOutY",file="stock_zoom-out.ppm", - command=zoom, argument=(self,-1,'y'),type="repeat") - - self.bSave = FLTKButton( - text="Save", file="stock_save_as.ppm", - command=save_figure, argument=self) - - self._group.end() - - def widget(self): - return self._group - - def close(self): - Gcf.destroy(self.figman._num) - - def set_active(self, ind): - self._ind = ind - self._active = [ self._axes[i] for i in self._ind ] - - def update(self): - self._axes = self.canvas.figure.axes - naxes = len(self._axes) - self.omenu.adjust(naxes) - -def pan(ptr, arg): - base,direction,axe=arg - for a in base._active: - if(axe=='x'): - a.panx(direction) - else: - a.pany(direction) - base.figman.show() - -def zoom(ptr, arg): - base,direction,axe=arg - for a in base._active: - if(axe=='x'): - a.zoomx(direction) - else: - a.zoomy(direction) - base.figman.show() - - - -def save_figure(ptr,base): - filetypes = base.canvas.get_supported_filetypes() - default_filetype = base.canvas.get_default_filetype() - sorted_filetypes = filetypes.items() - sorted_filetypes.sort() - - selected_filter = 0 - filters = [] - for i, (ext, name) in enumerate(sorted_filetypes): - filter = '%s (*.%s)' % (name, ext) - filters.append(filter) - if ext == default_filetype: - selected_filter = i - filters = '\t'.join(filters) - - file_chooser=base._fc - file_chooser.filter(filters) - file_chooser.filter_value(selected_filter) - file_chooser.show() - while file_chooser.visible() : - Fltk.Fl.wait() - fname=None - if(file_chooser.count() and file_chooser.value(0) != None): - fname="" - (status,fname)=Fltk.fl_filename_absolute(fname, 1024, file_chooser.value(0)) - - if fname is None: # Cancel - return - #start from last directory - lastDir = os.path.dirname(fname) - file_chooser.directory(lastDir) - format = sorted_filetypes[file_chooser.filter_value()][0] - - try: - base.canvas.print_figure(fname, format=format) - except IOError as msg: - err = '\n'.join(map(str, msg)) - msg = 'Failed to save %s: Error msg was\n\n%s' % ( - fname, err) - error_msg_fltk(msg) - -class NavigationToolbar2FltkAgg(NavigationToolbar2): - """ - Public attriubutes - - canvas - the FigureCanvas - figman - the Figure manager - - """ - - def __init__(self, canvas, figman): - self.canvas = canvas - self.figman = figman - NavigationToolbar2.__init__(self, canvas) - self.pan_selected=False - self.zoom_selected=False - - def set_cursor(self, cursor): - Fltk.fl_cursor(cursord[cursor],Fltk.FL_BLACK,Fltk.FL_WHITE) - - def dynamic_update(self): - self.canvas.draw() - - def pan(self,*args): - self.pan_selected=not self.pan_selected - self.zoom_selected = False - self.canvas.canvas._draw_overlay= False - if self.pan_selected: - self.bPan.widget().value(1) - else: - self.bPan.widget().value(0) - if self.zoom_selected: - self.bZoom.widget().value(1) - else: - self.bZoom.widget().value(0) - NavigationToolbar2.pan(self,args) - - def zoom(self,*args): - self.zoom_selected=not self.zoom_selected - self.canvas.canvas._draw_overlay=self.zoom_selected - self.pan_selected = False - if self.pan_selected: - self.bPan.widget().value(1) - else: - self.bPan.widget().value(0) - if self.zoom_selected: - self.bZoom.widget().value(1) - else: - self.bZoom.widget().value(0) - NavigationToolbar2.zoom(self,args) - - def configure_subplots(self,*args): - window = Fltk.Fl_Double_Window(100,100,480,240) - toolfig = Figure(figsize=(6,3)) - canvas = FigureCanvasFltkAgg(toolfig) - window.end() - toolfig.subplots_adjust(top=0.9) - tool = SubplotTool(self.canvas.figure, toolfig) - window.show() - canvas.show() - - def _init_toolbar(self): - Fltk.Fl_File_Icon.load_system_icons() - self._fc = Fltk.Fl_File_Chooser( ".", "*", Fltk.Fl_File_Chooser.CREATE, "Save Figure" ) - self._fc.hide() - t1,t2,w,h = self.canvas.figure.bbox.bounds - w, h = int(w), int(h) - self._group = Fltk.Fl_Pack(0,h+2,1000,26) - self._group.type(Fltk.FL_HORIZONTAL) - self._axes=self.canvas.figure.axes - self.naxes = len(self._axes) - self.omenu = AxisMenu( toolbar=self) - - self.bHome = FLTKButton( - text="Home", file="home.ppm", - command=self.home,argument=self) - - self.bBack = FLTKButton( - text="Back", file="back.ppm", - command=self.back,argument=self) - - self.bForward = FLTKButton( - text="Forward", file="forward.ppm", - command=self.forward,argument=self) - - self.bPan = FLTKButton( - text="Pan/Zoom",file="move.ppm", - command=self.pan,argument=self,type="pushed") - - self.bZoom = FLTKButton( - text="Zoom to rectangle",file="zoom_to_rect.ppm", - command=self.zoom,argument=self,type="pushed") - - - self.bsubplot = FLTKButton( text="Configure Subplots", file="subplots.ppm", - command = self.configure_subplots,argument=self,type="pushed") - self.bSave = FLTKButton( - text="Save", file="filesave.ppm", - command=save_figure, argument=self) - - self._group.end() - self.message = Fltk.Fl_Output(0,0,w,8) - self._group.add_resizable(self.message) - self.update() - - def widget(self): - return self._group - - def close(self): - Gcf.destroy(self.figman._num) - - def set_active(self, ind): - self._ind = ind - self._active = [ self._axes[i] for i in self._ind ] - - def update(self): - self._axes = self.canvas.figure.axes - naxes = len(self._axes) - self.omenu.adjust(naxes) - NavigationToolbar2.update(self) - - def set_message(self, s): - self.message.value(s) - - - -FigureManager = FigureManagerFltkAgg diff --git a/lib/matplotlib/backends/windowing.py b/lib/matplotlib/backends/windowing.py index 6cba0d8e6ed7..413058fcbb37 100644 --- a/lib/matplotlib/backends/windowing.py +++ b/lib/matplotlib/backends/windowing.py @@ -1,5 +1,5 @@ """ -MS Windows-specific helper for TkAgg and FltkAgg backends. +MS Windows-specific helper for the TkAgg backend. With rcParams['tk.window_focus'] default of False, it is effectively disabled. diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index 0ee7224cf5a1..4210cd8f7de5 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -20,12 +20,12 @@ from matplotlib.fontconfig_pattern import parse_fontconfig_pattern from matplotlib.colors import is_color_like -#interactive_bk = ['gtk', 'gtkagg', 'gtkcairo', 'fltkagg', 'qtagg', 'qt4agg', +#interactive_bk = ['gtk', 'gtkagg', 'gtkcairo', 'qtagg', 'qt4agg', # 'tkagg', 'wx', 'wxagg', 'cocoaagg', 'webagg'] # The capitalized forms are needed for ipython at present; this may # change for later versions. -interactive_bk = ['GTK', 'GTKAgg', 'GTKCairo', 'FltkAgg', 'MacOSX', +interactive_bk = ['GTK', 'GTKAgg', 'GTKCairo', 'MacOSX', 'Qt4Agg', 'TkAgg', 'WX', 'WXAgg', 'CocoaAgg', 'GTK3Cairo', 'GTK3Agg', 'WebAgg'] From 4109e961685dc237cd83bf68a1bc1048a85f2c3b Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Mon, 20 May 2013 15:08:32 -0400 Subject: [PATCH 06/11] Remove the EMF backend --- doc/api/api_changes.rst | 2 + lib/matplotlib/backend_bases.py | 6 - lib/matplotlib/backends/backend_emf.py | 746 ------------------------- 3 files changed, 2 insertions(+), 752 deletions(-) delete mode 100644 lib/matplotlib/backends/backend_emf.py diff --git a/doc/api/api_changes.rst b/doc/api/api_changes.rst index 717ac5f48448..2585d3812452 100644 --- a/doc/api/api_changes.rst +++ b/doc/api/api_changes.rst @@ -24,6 +24,8 @@ Changes in 1.3.x - The Qt 3.x backends (`qt` and `qtagg`) have been removed in favor of the Qt 4.x backends (`qt4` and `qt4agg`). + - The FltkAgg and Emf backends have been removed. + - The `matplotlib.nxutils` module has been removed. Use the functionality on `matplotlib.path.Path.contains_point` and friends instead. diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 371f510b8093..b8c6dc8f97cb 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -1920,7 +1920,6 @@ def get_width_height(self): return int(self.figure.bbox.width), int(self.figure.bbox.height) filetypes = { - 'emf': 'Enhanced Metafile', 'eps': 'Encapsulated Postscript', 'pdf': 'Portable Document Format', 'pgf': 'LaTeX PGF Figure', @@ -1942,11 +1941,6 @@ def get_width_height(self): # >>> import matplotlib.tests.test_spines # >>> list(matplotlib.tests.test_spines.test_spines_axes_positions())[0][0]() - def print_emf(self, *args, **kwargs): - from backends.backend_emf import FigureCanvasEMF # lazy import - emf = self.switch_backends(FigureCanvasEMF) - return emf.print_emf(*args, **kwargs) - def print_eps(self, *args, **kwargs): from backends.backend_ps import FigureCanvasPS # lazy import ps = self.switch_backends(FigureCanvasPS) diff --git a/lib/matplotlib/backends/backend_emf.py b/lib/matplotlib/backends/backend_emf.py deleted file mode 100644 index 94194fe7814f..000000000000 --- a/lib/matplotlib/backends/backend_emf.py +++ /dev/null @@ -1,746 +0,0 @@ -""" -Enhanced Metafile backend. See http://pyemf.sourceforge.net for the EMF -driver library. -""" - -from __future__ import division, print_function - -try: - import pyemf -except ImportError: - raise ImportError('You must first install pyemf from http://pyemf.sf.net') - -import os,sys,math,re - -from matplotlib import verbose, __version__, rcParams - -from matplotlib._pylab_helpers import Gcf -from matplotlib.backend_bases import RendererBase, GraphicsContextBase,\ - FigureManagerBase, FigureCanvasBase -from matplotlib.figure import Figure -from matplotlib.transforms import Bbox - -from matplotlib.font_manager import findfont, FontProperties -from matplotlib.ft2font import FT2Font, KERNING_UNFITTED, KERNING_DEFAULT, KERNING_UNSCALED - -from matplotlib.path import Path -from matplotlib.transforms import Affine2D -from matplotlib.mlab import quad2cubic - -# Font handling stuff snarfed from backend_ps, but only using TTF fonts -_fontd = {} - -# Debug print stuff -debugHandle = False -debugPrint = False -debugText = False - -# Hashable font properties class. In EMF, angle of rotation is a part -# of the font properties, so a handle to a new font must be obtained -# if the rotation changes. -class EMFFontProperties(FontProperties): - def __init__(self,other,angle): - FontProperties.__init__(self,other.get_family(), - other.get_style(), - other.get_variant(), - other.get_weight(), - other.get_stretch(), - other.get_size()) - self._angle=angle - - def __hash__(self): - return hash( (FontProperties.__hash__(self), self._angle)) - - def __str__(self): - return str( (FontProperties.__str__(self), self._angle)) - - def set_angle(self,angle): - self._angle=angle - - def get_angle(self): - return self._angle - -# Hashable pen (line style) properties. -class EMFPen: - def __init__(self,emf,gc): - self.emf=emf - self.gc=gc - - r,g,b=gc.get_rgb()[:3] - self.r=int(r*255) - self.g=int(g*255) - self.b=int(b*255) - self.width=int(gc.get_linewidth()) - - self.style=0 - self.set_linestyle() - if debugHandle: print("EMFPen: style=%d width=%d rgb=(%d,%d,%d)" % (self.style,self.width,self.r,self.g,self.b)) - - def __hash__(self): - return hash((self.style,self.width,self.r,self.g,self.b)) - - def set_linestyle(self): - # Hack. Negative width lines will not get drawn. - if self.width<0: - self.style=pyemf.PS_NULL - else: - styles={'solid':pyemf.PS_SOLID, 'dashed':pyemf.PS_DASH, - 'dashdot':pyemf.PS_DASHDOT, 'dotted':pyemf.PS_DOT} - #style=styles.get(self.gc.get_linestyle('solid')) - style=self.gc.get_linestyle('solid') - if debugHandle: print("EMFPen: style=%s" % style) - if style in styles: - self.style=styles[style] - else: - self.style=pyemf.PS_SOLID - - def get_handle(self): - handle=self.emf.CreatePen(self.style,self.width,(self.r,self.g,self.b)) - return handle - -# Hashable brush (fill style) properties. -class EMFBrush: - def __init__(self,emf,rgb): - self.emf=emf - r,g,b=rgb[:3] - self.r=int(r*255) - self.g=int(g*255) - self.b=int(b*255) - if debugHandle: print("EMFBrush: rgb=(%d,%d,%d)" % (self.r,self.g,self.b)) - - def __hash__(self): - return hash((self.r,self.g,self.b)) - - def get_handle(self): - handle=self.emf.CreateSolidBrush((self.r,self.g,self.b)) - return handle - - - - -class RendererEMF(RendererBase): - """ - The renderer handles drawing/rendering operations through a - pyemf.EMF instance. - """ - - fontweights = { - 100 : pyemf.FW_NORMAL, - 200 : pyemf.FW_NORMAL, - 300 : pyemf.FW_NORMAL, - 400 : pyemf.FW_NORMAL, - 500 : pyemf.FW_NORMAL, - 600 : pyemf.FW_BOLD, - 700 : pyemf.FW_BOLD, - 800 : pyemf.FW_BOLD, - 900 : pyemf.FW_BOLD, - 'ultralight' : pyemf.FW_ULTRALIGHT, - 'light' : pyemf.FW_LIGHT, - 'normal' : pyemf.FW_NORMAL, - 'medium' : pyemf.FW_MEDIUM, - 'semibold' : pyemf.FW_SEMIBOLD, - 'bold' : pyemf.FW_BOLD, - 'heavy' : pyemf.FW_HEAVY, - 'ultrabold' : pyemf.FW_ULTRABOLD, - 'black' : pyemf.FW_BLACK, - } - - def __init__(self, outfile, width, height, dpi): - "Initialize the renderer with a gd image instance" - self.outfile = outfile - - # a map from get_color args to colors - self._cached = {} - - # dict of hashed properties to already created font handles - self._fontHandle = {} - - self.lastHandle = {'font':-1, 'pen':-1, 'brush':-1} - - self.emf=pyemf.EMF(width,height,dpi,'in') - - self.width=int(width*dpi) - self.height=int(height*dpi) - self.dpi = dpi - self.pointstodpi = dpi/72.0 - self.hackPointsForMathExponent = 2.0 - - # set background transparent for text - self.emf.SetBkMode(pyemf.TRANSPARENT) - # set baseline for text to be bottom left corner - self.emf.SetTextAlign( pyemf.TA_BOTTOM|pyemf.TA_LEFT) - - self._lastClipRect = None - - if debugPrint: print("RendererEMF: (%f,%f) %s dpi=%f" % (self.width,self.height,outfile,dpi)) - - - - def save(self): - self.emf.save(self.outfile) - - def draw_arc(self, gcEdge, rgbFace, x, y, width, height, angle1, angle2, rotation): - """ - Draw an arc using GraphicsContext instance gcEdge, centered at x,y, - with width and height and angles from 0.0 to 360.0 - 0 degrees is at 3-o'clock - positive angles are anti-clockwise - - If the color rgbFace is not None, fill the arc with it. - """ - if debugPrint: print("draw_arc: (%f,%f) angles=(%f,%f) w,h=(%f,%f)" % (x,y,angle1,angle2,width,height)) - pen=self.select_pen(gcEdge) - brush=self.select_brush(rgbFace) - - # This algorithm doesn't work very well on small circles - # because of rounding error. This shows up most obviously on - # legends where the circles are small anyway, and it is - # compounded by the fact that it puts several circles right - # next to each other so the differences are obvious. - hw=width/2 - hh=height/2 - x1=int(x-width/2) - y1=int(y-height/2) - if brush: - self.emf.Pie(int(x-hw),int(self.height-(y-hh)),int(x+hw),int(self.height-(y+hh)),int(x+math.cos(angle1*math.pi/180.0)*hw),int(self.height-(y+math.sin(angle1*math.pi/180.0)*hh)),int(x+math.cos(angle2*math.pi/180.0)*hw),int(self.height-(y+math.sin(angle2*math.pi/180.0)*hh))) - else: - self.emf.Arc(int(x-hw),int(self.height-(y-hh)),int(x+hw),int(self.height-(y+hh)),int(x+math.cos(angle1*math.pi/180.0)*hw),int(self.height-(y+math.sin(angle1*math.pi/180.0)*hh)),int(x+math.cos(angle2*math.pi/180.0)*hw),int(self.height-(y+math.sin(angle2*math.pi/180.0)*hh))) - - - def handle_clip_rectangle(self, gc): - new_bounds = gc.get_clip_rectangle() - if new_bounds is not None: - new_bounds = new_bounds.bounds - if self._lastClipRect != new_bounds: - self._lastClipRect = new_bounds - if new_bounds is None: - # use the maximum rectangle to disable clipping - x, y, width, height = (0, 0, self.width, self.height) - else: - x, y, width, height = new_bounds - self.emf.BeginPath() - self.emf.MoveTo(int(x), int(self.height - y)) - self.emf.LineTo(int(x) + int(width), int(self.height - y)) - self.emf.LineTo(int(x) + int(width), int(self.height - y) - int(height)) - self.emf.LineTo(int(x), int(self.height - y) - int(height)) - self.emf.CloseFigure() - self.emf.EndPath() - self.emf.SelectClipPath() - - - def convert_path(self, tpath): - self.emf.BeginPath() - last_points = None - for points, code in tpath.iter_segments(): - if code == Path.MOVETO: - self.emf.MoveTo(*points) - elif code == Path.CLOSEPOLY: - self.emf.CloseFigure() - elif code == Path.LINETO: - self.emf.LineTo(*points) - elif code == Path.CURVE3: - points = quad2cubic(*(list(last_points[-2:]) + list(points))) - self.emf.PolyBezierTo(zip(points[2::2], points[3::2])) - elif code == Path.CURVE4: - self.emf.PolyBezierTo(zip(points[::2], points[1::2])) - last_points = points - self.emf.EndPath() - - - def draw_path(self, gc, path, transform, rgbFace=None): - """ - Draws a :class:`~matplotlib.path.Path` instance using the - given affine transform. - """ - self.handle_clip_rectangle(gc) - gc._rgb = gc._rgb[:3] - self.select_pen(gc) - self.select_brush(rgbFace) - transform = transform + Affine2D().scale(1.0, -1.0).translate(0.0, self.height) - tpath = transform.transform_path(path) - self.convert_path(tpath) - if rgbFace is None: - self.emf.StrokePath() - else: - self.emf.StrokeAndFillPath() - - - def draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None): - """ - Draw the Image instance into the current axes; x is the - distance in pixels from the left hand side of the canvas. y is - the distance from the origin. That is, if origin is upper, y - is the distance from top. If origin is lower, y is the - distance from bottom - - bbox is a matplotlib.transforms.BBox instance for clipping, or - None - """ - - # pyemf2 currently doesn't support bitmaps. - - pass - - - def draw_line(self, gc, x1, y1, x2, y2): - """ - Draw a single line from x1,y1 to x2,y2 - """ - if debugPrint: print("draw_line: (%f,%f) - (%f,%f)" % (x1,y1,x2,y2)) - - if self.select_pen(gc): - self.emf.Polyline([(long(x1),long(self.height-y1)),(long(x2),long(self.height-y2))]) - else: - if debugPrint: print("draw_line: optimizing away (%f,%f) - (%f,%f)" % (x1,y1,x2,y2)) - - def draw_lines(self, gc, x, y): - """ - x and y are equal length arrays, draw lines connecting each - point in x, y - """ - if debugPrint: print("draw_lines: %d points" % len(str(x))) - - # optimize away anything that won't actually be drawn. Edge - # style must not be PS_NULL for it to appear on screen. - if self.select_pen(gc): - points = [(long(x[i]), long(self.height-y[i])) for i in range(len(x))] - self.emf.Polyline(points) - - def draw_point(self, gc, x, y): - """ - Draw a single point at x,y - Where 'point' is a device-unit point (or pixel), not a matplotlib point - """ - if debugPrint: print("draw_point: (%f,%f)" % (x,y)) - - # don't cache this pen - pen=EMFPen(self.emf,gc) - - self.emf.SetPixel(long(x),long(self.height-y),(pen.r,pen.g,pen.b)) - - def draw_polygon(self, gcEdge, rgbFace, points): - """ - Draw a polygon using the GraphicsContext instance gc. - points is a len vertices tuple, each element - giving the x,y coords a vertex - - If the color rgbFace is not None, fill the polygon with it - """ - if debugPrint: print("draw_polygon: %d points" % len(points)) - - # optimize away anything that won't actually draw. Either a - # face color or edge style must be defined - pen=self.select_pen(gcEdge) - brush=self.select_brush(rgbFace) - if pen or brush: - points = [(long(x), long(self.height-y)) for x,y in points] - self.emf.Polygon(points) - else: - points = [(long(x), long(self.height-y)) for x,y in points] - if debugPrint: print("draw_polygon: optimizing away polygon: %d points = %s" % (len(points),str(points))) - - def draw_rectangle(self, gcEdge, rgbFace, x, y, width, height): - """ - Draw a non-filled rectangle using the GraphicsContext instance gcEdge, - with lower left at x,y with width and height. - - If rgbFace is not None, fill the rectangle with it. - """ - if debugPrint: print("draw_rectangle: (%f,%f) w=%f,h=%f" % (x,y,width,height)) - - # optimize away anything that won't actually draw. Either a - # face color or edge style must be defined - pen=self.select_pen(gcEdge) - brush=self.select_brush(rgbFace) - if pen or brush: - self.emf.Rectangle(int(x),int(self.height-y),int(x)+int(width),int(self.height-y)-int(height)) - else: - if debugPrint: print("draw_rectangle: optimizing away (%f,%f) w=%f,h=%f" % (x,y,width,height)) - - - def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): - """ - Draw the text.Text instance s at x,y (display coords) with font - properties instance prop at angle in degrees, using GraphicsContext gc - - **backend implementers note** - - When you are trying to determine if you have gotten your bounding box - right (which is what enables the text layout/alignment to work - properly), it helps to change the line in text.py - - if 0: bbox_artist(self, renderer) - - to if 1, and then the actual bounding box will be blotted along with - your text. - """ - if ismath: s = self.strip_math(s) - self.handle_clip_rectangle(gc) - self.emf.SetTextColor(gc.get_rgb()[:3]) - self.select_font(prop,angle) - if isinstance(s, unicode): - # unicode characters do not seem to work with pyemf - try: - s = s.replace(u'\u2212', '-').encode('iso-8859-1') - except UnicodeEncodeError: - pass - self.emf.TextOut(x,y,s) - - - def draw_plain_text(self, gc, x, y, s, prop, angle): - """ - Draw a text string verbatim; no conversion is done. - """ - if debugText: print("draw_plain_text: (%f,%f) %d degrees: '%s'" % (x,y,angle,s)) - if debugText: print(" properties:\n"+str(prop)) - self.select_font(prop,angle) - - # haxor follows! The subtleties of text placement in EMF - # still elude me a bit. It always seems to be too high on the - # page, about 10 pixels too high on a 300dpi resolution image. - # So, I'm adding this hack for the moment: - hackoffsetper300dpi=10 - xhack=math.sin(angle*math.pi/180.0)*hackoffsetper300dpi*self.dpi/300.0 - yhack=math.cos(angle*math.pi/180.0)*hackoffsetper300dpi*self.dpi/300.0 - - self.emf.TextOut(long(x+xhack),long(y+yhack),s) - - - def draw_math_text(self, gc, x, y, s, prop, angle): - """ - Draw a subset of TeX, currently handles exponents only. Since - pyemf doesn't have any raster functionality yet, the - texmanager.get_rgba won't help. - """ - if debugText: print("draw_math_text: (%f,%f) %d degrees: '%s'" % (x,y,angle,s)) - - s = s[1:-1] # strip the $ from front and back - match=re.match("10\^\{(.+)\}",s) - if match: - exp=match.group(1) - if debugText: print(" exponent=%s" % exp) - font = self._get_font_ttf(prop) - font.set_text("10", 0.0) - w, h = font.get_width_height() - w /= 64.0 # convert from subpixels - h /= 64.0 - self.draw_plain_text(gc,x,y,"10",prop,angle) - propexp=prop.copy() - propexp.set_size(prop.get_size_in_points()*.8) - self.draw_plain_text(gc,x+w+self.points_to_pixels(self.hackPointsForMathExponent),y-(h/2),exp,propexp,angle) - else: - # if it isn't an exponent, then render the raw TeX string. - self.draw_plain_text(gc,x,y,s,prop,angle) - - def get_math_text_width_height(self, s, prop): - """ - get the width and height in display coords of the string s - with FontPropertry prop, ripped right out of backend_ps. This - method must be kept in sync with draw_math_text. - """ - if debugText: print("get_math_text_width_height:") - s = s[1:-1] # strip the $ from front and back - match=re.match("10\^\{(.+)\}",s) - if match: - exp=match.group(1) - if debugText: print(" exponent=%s" % exp) - font = self._get_font_ttf(prop) - font.set_text("10", 0.0) - w1, h1 = font.get_width_height() - - propexp=prop.copy() - propexp.set_size(prop.get_size_in_points()*.8) - fontexp=self._get_font_ttf(propexp) - fontexp.set_text(exp, 0.0) - w2, h2 = fontexp.get_width_height() - w=w1+w2 - h=h1+(h2/2) - w /= 64.0 # convert from subpixels - h /= 64.0 - w+=self.points_to_pixels(self.hackPointsForMathExponent) - if debugText: print(" math string=%s w,h=(%f,%f)" % (s, w, h)) - else: - w,h=self.get_text_width_height(s,prop,False) - return w, h - - - def get_text_width_height_descent(self, s, prop, ismath): - """ - get the width and height in display coords of the string s - with FontPropertry prop - """ - if ismath: s = self.strip_math(s) - font = self._get_font_ttf(prop) - font.set_text(s, 0.0) - w, h = font.get_width_height() - w /= 64.0 # convert from subpixels - h /= 64.0 - d = font.get_descent() - d /= 64.0 - return w, h, d - - - def flipy(self): - """return true if y small numbers are top for renderer - Is used for drawing text (text.py) and images (image.py) only - """ - return True - - - def get_canvas_width_height(self): - """ - return the canvas width and height in display coords - """ - return self.width,self.height - - - def set_handle(self,type,handle): - """ - Update the EMF file with the current handle, but only if it - isn't the same as the last one. Don't want to flood the file - with duplicate info. - """ - if self.lastHandle[type] != handle: - self.emf.SelectObject(handle) - self.lastHandle[type]=handle - - - def get_font_handle(self, prop, angle): - """ - Look up the handle for the font based on the dict of - properties *and* the rotation angle, since in EMF the font - rotation is a part of the font definition. - """ - prop=EMFFontProperties(prop,angle) - size=int(prop.get_size_in_points()*self.pointstodpi) - face=prop.get_name() - key = hash(prop) - handle = self._fontHandle.get(key) - if handle is None: - handle=self.emf.CreateFont(-size, 0, int(angle)*10, int(angle)*10, - self.fontweights.get(prop.get_weight(), pyemf.FW_NORMAL), - int(prop.get_style() == 'italic'), - 0, 0, - pyemf.ANSI_CHARSET, pyemf.OUT_DEFAULT_PRECIS, - pyemf.CLIP_DEFAULT_PRECIS, pyemf.DEFAULT_QUALITY, - pyemf.DEFAULT_PITCH | pyemf.FF_DONTCARE, face); - if debugHandle: print("get_font_handle: creating handle=%d for face=%s size=%d" % (handle,face,size)) - self._fontHandle[key]=handle - if debugHandle: print(" found font handle %d for face=%s size=%d" % (handle,face,size)) - self.set_handle("font",handle) - return handle - - - def select_font(self,prop,angle): - handle=self.get_font_handle(prop,angle) - self.set_handle("font",handle) - - - def select_pen(self, gc): - """ - Select a pen that includes the color, line width and line - style. Return the pen if it will draw a line, or None if the - pen won't produce any output (i.e. the style is PS_NULL) - """ - pen=EMFPen(self.emf,gc) - key=hash(pen) - handle=self._fontHandle.get(key) - if handle is None: - handle=pen.get_handle() - self._fontHandle[key]=handle - if debugHandle: print(" found pen handle %d" % handle) - self.set_handle("pen",handle) - if pen.style != pyemf.PS_NULL: - return pen - else: - return None - - - def select_brush(self, rgb): - """ - Select a fill color, and return the brush if the color is - valid or None if this won't produce a fill operation. - """ - if rgb is not None: - brush=EMFBrush(self.emf,rgb) - key=hash(brush) - handle=self._fontHandle.get(key) - if handle is None: - handle=brush.get_handle() - self._fontHandle[key]=handle - if debugHandle: print(" found brush handle %d" % handle) - self.set_handle("brush",handle) - return brush - else: - return None - - - def _get_font_ttf(self, prop): - """ - get the true type font properties, used because EMFs on - windows will use true type fonts. - """ - key = hash(prop) - font = _fontd.get(key) - if font is None: - fname = findfont(prop) - if debugText: print("_get_font_ttf: name=%s" % fname) - font = FT2Font(str(fname)) - _fontd[key] = font - font.clear() - size = prop.get_size_in_points() - font.set_size(size, self.dpi) - - return font - - - def get_text_width_height(self, s, prop, ismath): - """ - get the width and height in display coords of the string s - with FontPropertry prop, ripped right out of backend_ps - """ - if debugText: print("get_text_width_height: ismath=%s properties: %s" % (str(ismath),str(prop))) - if ismath: - if debugText: print(" MATH TEXT! = %s" % str(ismath)) - w,h = self.get_math_text_width_height(s, prop) - return w,h - - font = self._get_font_ttf(prop) - font.set_text(s, 0.0) - w, h = font.get_width_height() - w /= 64.0 # convert from subpixels - h /= 64.0 - if debugText: print(" text string=%s w,h=(%f,%f)" % (s, w, h)) - return w, h - - - def new_gc(self): - return GraphicsContextEMF() - - def points_to_pixels(self, points): - # if backend doesn't have dpi, eg, postscript or svg - #return points - # elif backend assumes a value for pixels_per_inch - #return points/72.0 * self.dpi.get() * pixels_per_inch/72.0 - # else - return points/72.0 * self.dpi - - -class GraphicsContextEMF(GraphicsContextBase): - """ - The graphics context provides the color, line styles, etc... See the gtk - and postscript backends for examples of mapping the graphics context - attributes (cap styles, join styles, line widths, colors) to a particular - backend. In GTK this is done by wrapping a gtk.gdk.GC object and - forwarding the appropriate calls to it using a dictionary mapping styles - to gdk constants. In Postscript, all the work is done by the renderer, - mapping line styles to postscript calls. - - If it's more appropriate to do the mapping at the renderer level (as in - the postscript backend), you don't need to override any of the GC methods. - If it's more appropriate to wrap an instance (as in the GTK backend) and - do the mapping here, you'll need to override several of the setter - methods. - - The base GraphicsContext stores colors as a RGB tuple on the unit - interval, eg, (0.5, 0.0, 1.0). You may need to map this to colors - appropriate for your backend. - """ - pass - - - -######################################################################## -# -# The following functions and classes are for pylab and implement -# window/figure managers, etc... -# -######################################################################## - -def draw_if_interactive(): - """ - For image backends - is not required - For GUI backends - this should be overriden if drawing should be done in - interactive python mode - """ - pass - -def show(): - """ - For image backends - is not required - For GUI backends - show() is usually the last line of a pylab script and - tells the backend that it is time to draw. In interactive mode, this may - be a do nothing func. See the GTK backend for an example of how to handle - interactive versus batch mode - """ - for manager in Gcf.get_all_fig_managers(): - # do something to display the GUI - pass - - -def new_figure_manager(num, *args, **kwargs): - """ - Create a new figure manager instance - """ - # if a main-level app must be created, this is the usual place to - # do it -- see backend_wx, backend_wxagg and backend_tkagg for - # examples. Not all GUIs require explicit instantiation of a - # main-level app (egg backend_gtk, backend_gtkagg) for pylab - FigureClass = kwargs.pop('FigureClass', Figure) - thisFig = FigureClass(*args, **kwargs) - return new_figure_manager_given_figure(num, thisFig) - - -def new_figure_manager_given_figure(num, figure): - """ - Create a new figure manager instance for the given figure. - """ - canvas = FigureCanvasEMF(figure) - manager = FigureManagerEMF(canvas, num) - return manager - - -class FigureCanvasEMF(FigureCanvasBase): - """ - The canvas the figure renders into. Calls the draw and print fig - methods, creates the renderers, etc... - - Public attribute - - figure - A Figure instance - """ - - def draw(self): - """ - Draw the figure using the renderer - """ - pass - - filetypes = {'emf': 'Enhanced Metafile'} - - def print_emf(self, filename, dpi=300, **kwargs): - width, height = self.figure.get_size_inches() - renderer = RendererEMF(filename,width,height,dpi) - self.figure.draw(renderer) - renderer.save() - - def get_default_filetype(self): - return 'emf' - -class FigureManagerEMF(FigureManagerBase): - """ - Wrap everything up into a window for the pylab interface - - For non interactive backends, the base class does all the work - """ - pass - -######################################################################## -# -# Now just provide the standard names that backend.__init__ is expecting -# -######################################################################## - - -FigureManager = FigureManagerEMF - From 965f6c41286e0a1edc016127928f3e4e7f3e7528 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Mon, 20 May 2013 18:27:39 -0400 Subject: [PATCH 07/11] Remove FltkAgg from comment --- matplotlibrc.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/matplotlibrc.template b/matplotlibrc.template index e1360b80f0dc..10558321c2f4 100644 --- a/matplotlibrc.template +++ b/matplotlibrc.template @@ -24,8 +24,8 @@ #### CONFIGURATION BEGINS HERE # the default backend; one of GTK GTKAgg GTKCairo GTK3Agg GTK3Cairo -# CocoaAgg FltkAgg MacOSX Qt4Agg TkAgg WX WXAgg Agg Cairo GDK PS -# PDF SVG Template +# CocoaAgg MacOSX Qt4Agg TkAgg WX WXAgg Agg Cairo GDK PS PDF SVG +# Template # You can also deploy your own backend outside of matplotlib by # referring to the module name (which must be in the PYTHONPATH) as # 'module://my_backend' From 21f394698a6ecdafc2f37b9d49926e8699f15975 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Mon, 20 May 2013 18:27:49 -0400 Subject: [PATCH 08/11] Fix typo --- doc/users/whats_new.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/users/whats_new.rst b/doc/users/whats_new.rst index fc3b5769a4f6..869bdd124186 100644 --- a/doc/users/whats_new.rst +++ b/doc/users/whats_new.rst @@ -27,9 +27,8 @@ Housecleaning A number of features that were deprecated in 1.2 or earlier, or have not been in a working state for a long time have been removed. -Highlights in include removing the Qt version 3 backends, and the -FltkAgg and Emf backends. See :ref:`changes_in_1_3` for a complete -list. +Highlights include removing the Qt version 3 backends, and the FltkAgg +and Emf backends. See :ref:`changes_in_1_3` for a complete list. `xkcd`-style sketch plotting ---------------------------- From 3dab37765d0d2fea4219eb723bcc3045d5d17fc6 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Mon, 20 May 2013 18:28:44 -0400 Subject: [PATCH 09/11] Remove commented out stuff --- lib/matplotlib/path.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/matplotlib/path.py b/lib/matplotlib/path.py index 4fff2807936f..bc3717f42007 100644 --- a/lib/matplotlib/path.py +++ b/lib/matplotlib/path.py @@ -10,11 +10,6 @@ from numpy import ma from matplotlib import _path - -# ._path import point_in_path, get_path_extents, \ -# point_in_path_collection, get_path_collection_extents, \ -# path_in_path, path_intersects_path, convert_path_to_polygons, \ -# cleanup_path, points_in_path, clip_path_to_rect from matplotlib.cbook import simple_linear_interpolation, maxdict from matplotlib import rcParams From 85f1966fe436a8541a059000919ecd4028164d22 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Mon, 20 May 2013 18:36:54 -0400 Subject: [PATCH 10/11] Remove misleading sentence. --- doc/api/api_changes.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/api/api_changes.rst b/doc/api/api_changes.rst index 2585d3812452..4f9c5c3b79e6 100644 --- a/doc/api/api_changes.rst +++ b/doc/api/api_changes.rst @@ -18,8 +18,7 @@ Changes in 1.3.x ================ * The following items that were deprecated in version 1.2 or earlier - have now been removed completely. Use the following mapping to - update your code: + have now been removed completely. - The Qt 3.x backends (`qt` and `qtagg`) have been removed in favor of the Qt 4.x backends (`qt4` and `qt4agg`). From cfdf9ecd69aff68ac5a17d95756ba3bd4adaa0d4 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Tue, 21 May 2013 06:41:27 -0400 Subject: [PATCH 11/11] Fix some typos pointed out in #2026. --- lib/matplotlib/cbook.py | 5 ++--- lib/matplotlib/mpl.py | 2 +- lib/matplotlib/rcsetup.py | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 225e2d657947..6772dccf7634 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -78,13 +78,12 @@ def warn_deprecated( since, message='', name='', alternative='', pending=False, obj_type='attribute'): """ - Used display deprecation warning in a standard way. + Used to display deprecation warning in a standard way. Parameters ------------ since : str - The release at which this API became deprecated. This is - required. + The release at which this API became deprecated. message : str, optional Override the default deprecation message. The format diff --git a/lib/matplotlib/mpl.py b/lib/matplotlib/mpl.py index 62051b86ec6b..2049ad9e2246 100644 --- a/lib/matplotlib/mpl.py +++ b/lib/matplotlib/mpl.py @@ -4,7 +4,7 @@ import warnings from matplotlib import cbook cbook.warn_deprecated( - '1.3', 'matplotlib.mpl', alterative='`import matplotlib as mpl`', + '1.3', 'matplotlib.mpl', alternative='`import matplotlib as mpl`', obj_type='module') from matplotlib import artist from matplotlib import axis diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index 4210cd8f7de5..7d54994c1c61 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -20,7 +20,7 @@ from matplotlib.fontconfig_pattern import parse_fontconfig_pattern from matplotlib.colors import is_color_like -#interactive_bk = ['gtk', 'gtkagg', 'gtkcairo', 'qtagg', 'qt4agg', +#interactive_bk = ['gtk', 'gtkagg', 'gtkcairo', 'qt4agg', # 'tkagg', 'wx', 'wxagg', 'cocoaagg', 'webagg'] # The capitalized forms are needed for ipython at present; this may # change for later versions.