From 03a663dad6975729da1d85691903cc7f607de730 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 9 Jan 2018 16:55:52 -0800 Subject: [PATCH] Various backend cleanups. Split out from the qt5cairo and wxcairo PRs. - GTK3: is_drawable is the same as mapped & visible (https://developer.gnome.org/gtk3/stable/GtkWidget.html#gtk-widget-is-drawable) - Synchronous draw appears unnecessary on GTK3 based on tests (probably was only needed in early animation code). - Most of the Wx printout code has been removed in 84ffb60; this PR just deprecates some remnants of it. MenuButtonWx is likewise unused since the removal of "classic" toolbars in 4243470; this PR deprecates it. - Don't initialize the wx statusbar to "None" (which just looks strange) -- other backends just start with an empty status text. - _convert_agg_to_wx_image is unused; _WX28_clipped_agg_as_bitmap can reasonably be inlined into _convert_agg_to_wx_bitmap. - Remove some unused private tk apis. - Simplify toolkit mocking in the docs. In particular pycairo is Py3-compatible only since 1.11.0 so the version_check is unneeded. - Removed the old list of deprecations (18-02-15) that has already been integrated into the 3.0 release notes in favor of a new list (for 3.1). --- .../2018-08-17-AL-deprecations.rst | 9 +++ lib/matplotlib/backends/_backend_tk.py | 21 ++----- lib/matplotlib/backends/backend_agg.py | 7 +-- lib/matplotlib/backends/backend_gtk3.py | 5 +- lib/matplotlib/backends/backend_gtk3cairo.py | 3 +- lib/matplotlib/backends/backend_qt5agg.py | 6 +- lib/matplotlib/backends/backend_wx.py | 47 +++++--------- lib/matplotlib/backends/backend_wxagg.py | 63 +++++-------------- 8 files changed, 52 insertions(+), 109 deletions(-) create mode 100644 doc/api/api_changes/2018-08-17-AL-deprecations.rst diff --git a/doc/api/api_changes/2018-08-17-AL-deprecations.rst b/doc/api/api_changes/2018-08-17-AL-deprecations.rst new file mode 100644 index 000000000000..05270577cf84 --- /dev/null +++ b/doc/api/api_changes/2018-08-17-AL-deprecations.rst @@ -0,0 +1,9 @@ +API deprecations +```````````````` + +The following API elements are deprecated: + +- ``backend_gtk3cairo.FigureCanvasGTK3Cairo``, +- ``backend_wx.debug_on_error``, ``backend_wx.fake_stderr``, + ``backend_wx.raise_msg_to_str``, ``backend_wx.MenuButtonWx``, + ``backend_wx.PrintoutWx``, diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py index 8be83044bcd6..8e1a2bb10bde 100644 --- a/lib/matplotlib/backends/_backend_tk.py +++ b/lib/matplotlib/backends/_backend_tk.py @@ -1,19 +1,17 @@ -import math +from contextlib import contextmanager import logging +import math import os.path import sys import tkinter as tk from tkinter.simpledialog import SimpleDialog import tkinter.filedialog import tkinter.messagebox -from contextlib import contextmanager import numpy as np -from . import _tkagg - import matplotlib -from matplotlib import backend_tools, rcParams +from matplotlib import backend_tools, cbook, rcParams from matplotlib.backend_bases import ( _Backend, FigureCanvasBase, FigureManagerBase, NavigationToolbar2, StatusbarBase, TimerBase, ToolContainerBase, cursors) @@ -21,6 +19,7 @@ from matplotlib._pylab_helpers import Gcf from matplotlib.figure import Figure from matplotlib.widgets import SubplotTool +from . import _tkagg try: from ._tkagg import Win32_GetForegroundWindow, Win32_SetForegroundWindow @@ -56,18 +55,6 @@ def _restore_foreground_window_at_end(): } -def raise_msg_to_str(msg): - """msg is a return arg from a raise. Join with new lines""" - if not isinstance(msg, str): - msg = '\n'.join(map(str, msg)) - return msg - - -def error_msg_tkpaint(msg, parent=None): - import tkinter.messagebox - tkinter.messagebox.showerror("matplotlib", msg) - - def blit(photoimage, aggimage, offsets, bbox=None): """ Blit *aggimage* to *photoimage*. diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index 2d1682608a89..50375e581a30 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -385,16 +385,11 @@ def draw(self): Draw the figure using the renderer. """ self.renderer = self.get_renderer(cleared=True) - # acquire a lock on the shared font cache - RendererAgg.lock.acquire() - - try: + with RendererAgg.lock: self.figure.draw(self.renderer) # A GUI class may be need to update a window using this draw, so # don't forget to call the superclass. super().draw() - finally: - RendererAgg.lock.release() def get_renderer(self, cleared=False): l, b, w, h = self.figure.bbox.bounds diff --git a/lib/matplotlib/backends/backend_gtk3.py b/lib/matplotlib/backends/backend_gtk3.py index 961d567c68ca..e6c18d528f46 100644 --- a/lib/matplotlib/backends/backend_gtk3.py +++ b/lib/matplotlib/backends/backend_gtk3.py @@ -293,11 +293,8 @@ def on_draw_event(self, widget, ctx): pass def draw(self): - if self.get_visible() and self.get_mapped(): + if self.is_drawable(): self.queue_draw() - # do a synchronous draw (its less efficient than an async draw, - # but is required if/when animation is used) - self.get_property("window").process_updates(False) def draw_idle(self): if self._idle_draw_id != 0: diff --git a/lib/matplotlib/backends/backend_gtk3cairo.py b/lib/matplotlib/backends/backend_gtk3cairo.py index 9d0fc16a3044..fae367a79cb3 100644 --- a/lib/matplotlib/backends/backend_gtk3cairo.py +++ b/lib/matplotlib/backends/backend_gtk3cairo.py @@ -1,5 +1,6 @@ from . import backend_cairo, backend_gtk3 from .backend_gtk3 import Gtk, _BackendGTK3 +from matplotlib import cbook from matplotlib.backend_bases import cursors @@ -35,6 +36,7 @@ def on_draw_event(self, widget, ctx): return False # finish event propagation? +@cbook.deprecated("3.1", alternative="backend_gtk3.FigureManagerGTK3") class FigureManagerGTK3Cairo(backend_gtk3.FigureManagerGTK3): pass @@ -42,4 +44,3 @@ class FigureManagerGTK3Cairo(backend_gtk3.FigureManagerGTK3): @_BackendGTK3.export class _BackendGTK3Cairo(_BackendGTK3): FigureCanvas = FigureCanvasGTK3Cairo - FigureManager = FigureManagerGTK3Cairo diff --git a/lib/matplotlib/backends/backend_qt5agg.py b/lib/matplotlib/backends/backend_qt5agg.py index eadb0138fa5c..4dec3b9cb0c4 100644 --- a/lib/matplotlib/backends/backend_qt5agg.py +++ b/lib/matplotlib/backends/backend_qt5agg.py @@ -1,5 +1,5 @@ """ -Render to qt from agg +Render to qt from agg. """ import ctypes @@ -31,8 +31,8 @@ def paintEvent(self, event): return self._draw_idle() # Only does something if a draw is pending. - # if the canvas does not have a renderer, then give up and wait for - # FigureCanvasAgg.draw(self) to be called + # If the canvas does not have a renderer, then give up and wait for + # FigureCanvasAgg.draw(self) to be called. if not hasattr(self, 'renderer'): return diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index 22801fb5f937..c9ff06e0bb8a 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -20,13 +20,13 @@ StatusbarBase) from matplotlib.backend_bases import _has_pil +from matplotlib import cbook, rcParams, backend_tools from matplotlib._pylab_helpers import Gcf +from matplotlib.backend_managers import ToolManager from matplotlib.figure import Figure from matplotlib.path import Path from matplotlib.transforms import Affine2D from matplotlib.widgets import SubplotTool -from matplotlib.backend_managers import ToolManager -from matplotlib import cbook, rcParams, backend_tools import wx @@ -38,30 +38,25 @@ # traceback is performed, and pdb activated, for all uncaught exceptions in # this case _DEBUG = 5 -if _DEBUG < 5: - import traceback - import pdb _DEBUG_lvls = {1: 'Low ', 2: 'Med ', 3: 'High', 4: 'Error'} def DEBUG_MSG(string, lvl=3, o=None): if lvl >= _DEBUG: - cls = o.__class__ - # Jeremy, often times the commented line won't print but the - # one below does. I think WX is redefining stderr, damned - # beast - # print("%s- %s in %s" % (_DEBUG_lvls[lvl], string, cls), - # file=sys.stderr) - print("%s- %s in %s" % (_DEBUG_lvls[lvl], string, cls)) + print(f"{_DEBUG_lvls[lvl]}- {string} in {type(o)}") +@cbook.deprecated("3.1") def debug_on_error(type, value, tb): """Code due to Thomas Heller - published in Python Cookbook (O'Reilley)""" + import pdb + import traceback traceback.print_exception(type, value, tb) print() - pdb.pm() # jdh uncomment + pdb.pm() +@cbook.deprecated("3.1") class fake_stderr(object): """ Wx does strange things with stderr, as it makes the assumption that @@ -96,6 +91,7 @@ def error_msg_wx(msg, parent=None): return None +@cbook.deprecated("3.1") def raise_msg_to_str(msg): """msg is a return arg from a raise. Join with new lines.""" if not isinstance(msg, str): @@ -1091,22 +1087,16 @@ def _print_image(self, filename, filetype, *args, **kwargs): 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 + # Now that we have rendered into the bitmap, save it to the appropriate + # file type and clean up. if isinstance(filename, str): if not image.SaveFile(filename, filetype): - DEBUG_MSG('print_figure() file save error', 4, self) - raise RuntimeError( - 'Could not save figure to %s\n' % - (filename)) + raise RuntimeError(f'Could not save figure to {filename}') elif cbook.is_writable_file_like(filename): if not isinstance(image, wx.Image): image = image.ConvertToImage() if not image.SaveStream(filename, filetype): - DEBUG_MSG('print_figure() file save error', 4, self) - raise RuntimeError( - 'Could not save figure to %s\n' % - (filename)) + raise RuntimeError(f'Could not save figure to {filename}') # Restore everything to normal self.bitmap = origBitmap @@ -1316,6 +1306,7 @@ def _set_frame_icon(frame): frame.SetIcons(bundle) +@cbook.deprecated("3.1") class MenuButtonWx(wx.Button): """ wxPython does not permit a menu to be incorporated directly into a toolbar. @@ -1393,7 +1384,7 @@ def updateAxes(self, maxAxis): """Ensures that there are entries for max_axis axes in the menu (selected by default).""" if maxAxis > len(self._axisId): - for i in range(len(self._axisId) + 1, maxAxis + 1, 1): + for i in range(len(self._axisId) + 1, maxAxis + 1): menuId = wx.NewId() self._axisId.append(menuId) self._menu.Append(menuId, "Axis %d" % i, @@ -1638,16 +1629,10 @@ class StatusBarWx(wx.StatusBar): def __init__(self, parent, *args, **kwargs): wx.StatusBar.__init__(self, parent, -1) self.SetFieldsCount(2) - self.SetStatusText("None", 1) - # self.SetStatusText("Measurement: None", 2) - # self.Reposition() def set_function(self, string): self.SetStatusText("%s" % string, 1) - # def set_measurement(self, string): - # self.SetStatusText("Measurement: %s" % string, 2) - # tools for matplotlib.backend_managers.ToolManager: @@ -1956,6 +1941,7 @@ def trigger(self, *args, **kwargs): # < Additions for printing support: Matt Newville +@cbook.deprecated("3.1") class PrintoutWx(wx.Printout): """ Simple wrapper around wx Printout class -- all the real work @@ -2037,7 +2023,6 @@ def OnPrintPage(self, page): self.canvas.figure.dpi = fig_dpi self.canvas.draw() return True -# > @_Backend.export diff --git a/lib/matplotlib/backends/backend_wxagg.py b/lib/matplotlib/backends/backend_wxagg.py index 52b338f5538a..ccb168b39240 100644 --- a/lib/matplotlib/backends/backend_wxagg.py +++ b/lib/matplotlib/backends/backend_wxagg.py @@ -61,33 +61,11 @@ def blit(self, bbox=None): srcDC.SelectObject(wx.NullBitmap) self.gui_repaint() - filetypes = FigureCanvasAgg.filetypes - - -# agg/wxPython image conversion functions (wxPython >= 2.8) - -def _convert_agg_to_wx_image(agg, bbox): - """ - Convert the region of the agg buffer bounded by bbox to a wx.Image. If - bbox is None, the entire buffer is converted. - - Note: agg must be a backend_agg.RendererAgg instance. - """ - if bbox is None: - # agg => rgb -> image - image = wx.Image(int(agg.width), int(agg.height)) - image.SetData(agg.tostring_rgb()) - return image - else: - # agg => rgba buffer -> bitmap => clipped bitmap => image - return wx.ImageFromBitmap(_WX28_clipped_agg_as_bitmap(agg, bbox)) - def _convert_agg_to_wx_bitmap(agg, bbox): """ Convert the region of the agg buffer bounded by bbox to a wx.Bitmap. If bbox is None, the entire buffer is converted. - Note: agg must be a backend_agg.RendererAgg instance. """ if bbox is None: @@ -96,36 +74,27 @@ def _convert_agg_to_wx_bitmap(agg, bbox): agg.buffer_rgba()) else: # agg => rgba buffer -> bitmap => clipped bitmap - return _WX28_clipped_agg_as_bitmap(agg, bbox) + l, b, width, height = bbox.bounds + r = l + width + t = b + height + srcBmp = wx.Bitmap.FromBufferRGBA(int(agg.width), int(agg.height), + agg.buffer_rgba()) + srcDC = wx.MemoryDC() + srcDC.SelectObject(srcBmp) -def _WX28_clipped_agg_as_bitmap(agg, bbox): - """ - Convert the region of a the agg buffer bounded by bbox to a wx.Bitmap. - - Note: agg must be a backend_agg.RendererAgg instance. - """ - l, b, width, height = bbox.bounds - r = l + width - t = b + height - - srcBmp = wx.Bitmap.FromBufferRGBA(int(agg.width), int(agg.height), - agg.buffer_rgba()) - srcDC = wx.MemoryDC() - srcDC.SelectObject(srcBmp) - - destBmp = wx.Bitmap(int(width), int(height)) - destDC = wx.MemoryDC() - destDC.SelectObject(destBmp) + destBmp = wx.Bitmap(int(width), int(height)) + destDC = wx.MemoryDC() + destDC.SelectObject(destBmp) - x = int(l) - y = int(int(agg.height) - t) - destDC.Blit(0, 0, int(width), int(height), srcDC, x, y) + x = int(l) + y = int(int(agg.height) - t) + destDC.Blit(0, 0, int(width), int(height), srcDC, x, y) - srcDC.SelectObject(wx.NullBitmap) - destDC.SelectObject(wx.NullBitmap) + srcDC.SelectObject(wx.NullBitmap) + destDC.SelectObject(wx.NullBitmap) - return destBmp + return destBmp @_BackendWx.export