From eff92173d4c55ba09396429dfdccfbdbd5689008 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Mon, 2 Dec 2013 10:15:59 -0500 Subject: [PATCH 01/90] Don't free a font that hasn't been created --- src/ft2font.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index 0fb3b8b65825..673bdb2969c4 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -846,6 +846,7 @@ PYCXX_NOARGS_METHOD_DECL(FT2Font, get_path) FT2Font::FT2Font(Py::PythonClassInstance *self, Py::Tuple &args, Py::Dict &kwds) : Py::PythonClass(self, args, kwds), + face(NULL), image() { FT_Open_Args open_args; @@ -975,7 +976,9 @@ FT2Font::~FT2Font() { _VERBOSE("FT2Font::~FT2Font"); - FT_Done_Face(face); + if (face) { + FT_Done_Face(face); + } for (size_t i = 0; i < glyphs.size(); i++) { From ec2212e664619d8f1013fe20bc5757ab2ca41743 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Mon, 2 Dec 2013 15:28:54 -0500 Subject: [PATCH 02/90] Don't clear glyphs if the face was never created. --- src/ft2font.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index 673bdb2969c4..1d94adf26d40 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -978,11 +978,11 @@ FT2Font::~FT2Font() if (face) { FT_Done_Face(face); - } - for (size_t i = 0; i < glyphs.size(); i++) - { - FT_Done_Glyph(glyphs[i]); + for (size_t i = 0; i < glyphs.size(); i++) + { + FT_Done_Glyph(glyphs[i]); + } } } From cbb1d4c32d5ebc85e6e2b3d585cca16f77cb159e Mon Sep 17 00:00:00 2001 From: Werner F Bruhin Date: Tue, 11 Feb 2014 16:49:16 +0100 Subject: [PATCH 03/90] - get wx backends compatible with wxPython Phoenix --- lib/matplotlib/backends/backend_wx.py | 71 +++++++++++++++++------- lib/matplotlib/backends/backend_wxagg.py | 26 +++++++-- 2 files changed, 71 insertions(+), 26 deletions(-) diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index d99f04f07ea9..8a9043a3a18f 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -667,8 +667,6 @@ class FigureCanvasWx(FigureCanvasBase, wx.Panel): wx.WXK_DELETE : 'delete', wx.WXK_HOME : 'home', wx.WXK_END : 'end', - wx.WXK_PRIOR : 'pageup', - wx.WXK_NEXT : 'pagedown', wx.WXK_PAGEUP : 'pageup', wx.WXK_PAGEDOWN : 'pagedown', wx.WXK_NUMPAD0 : '0', @@ -691,8 +689,6 @@ class FigureCanvasWx(FigureCanvasBase, wx.Panel): wx.WXK_NUMPAD_RIGHT : 'right', wx.WXK_NUMPAD_DOWN : 'down', wx.WXK_NUMPAD_LEFT : 'left', - wx.WXK_NUMPAD_PRIOR : 'pageup', - wx.WXK_NUMPAD_NEXT : 'pagedown', wx.WXK_NUMPAD_PAGEUP : 'pageup', wx.WXK_NUMPAD_PAGEDOWN : 'pagedown', wx.WXK_NUMPAD_HOME : 'home', @@ -736,7 +732,10 @@ def do_nothing(*args, **kwargs): # Create the drawing bitmap - self.bitmap =wx.EmptyBitmap(w, h) + if 'phoenix' in wx.PlatformInfo: + self.bitmap =wx.Bitmap(w, h) + else: + self.bitmap =wx.EmptyBitmap(w, h) DEBUG_MSG("__init__() - bitmap w:%d h:%d" % (w,h), 2, self) # TODO: Add support for 'point' inspection and plot navigation. self._isDrawn = False @@ -873,7 +872,10 @@ def start_event_loop(self, timeout=0): bind(self, wx.EVT_TIMER, self.stop_event_loop, id=id) # Event loop handler for start/stop event loop - self._event_loop = wx.EventLoop() + if 'phoenix' in wx.PlatformInfo: + self._event_loop = wx.GUIEventLoop() + else: + self._event_loop = wx.EventLoop() self._event_loop.Run() timer.Stop() @@ -897,7 +899,7 @@ def _get_imagesave_wildcards(self): 'return the wildcard string for the filesave dialog' default_filetype = self.get_default_filetype() filetypes = self.get_supported_filetypes_grouped() - sorted_filetypes = list(six.iteritems(filetypes)) + sorted_filetypes = filetypes.items() sorted_filetypes.sort() wildcards = [] extensions = [] @@ -923,9 +925,12 @@ def gui_repaint(self, drawDC=None): if drawDC is None: drawDC=wx.ClientDC(self) - drawDC.BeginDrawing() - drawDC.DrawBitmap(self.bitmap, 0, 0) - drawDC.EndDrawing() + if 'phoenix' in wx.PlatformInfo: + drawDC.DrawBitmap(self.bitmap, 0, 0) + else: + drawDC.BeginDrawing() + drawDC.DrawBitmap(self.bitmap, 0, 0) + drawDC.EndDrawing() #wx.GetApp().Yield() else: pass @@ -979,7 +984,11 @@ def _print_image(self, filename, filetype, *args, **kwargs): width = int(math.ceil(width)) height = int(math.ceil(height)) - self.bitmap = wx.EmptyBitmap(width, height) + if 'phoenix' in wx.PlatformInfo: + self.bitmap =wx.Bitmap(width, height) + else: + self.bitmap =wx.EmptyBitmap(width, height) + renderer = RendererWx(self.bitmap, self.figure.dpi) gc = renderer.new_gc() @@ -1052,7 +1061,11 @@ def _onSize(self, evt): DEBUG_MSG("_onSize()", 2, self) # Create a new, correctly sized bitmap self._width, self._height = self.GetClientSize() - self.bitmap =wx.EmptyBitmap(self._width, self._height) + if 'phoenix' in wx.PlatformInfo: + self.bitmap =wx.Bitmap(self._width, self._height) + else: + self.bitmap =wx.EmptyBitmap(self._width, self._height) + self._isDrawn = False if self._width <= 1 or self._height <= 1: return # Empty figure @@ -1636,12 +1649,25 @@ def _init_toolbar(self): self.AddSeparator() continue self.wx_ids[text] = wx.NewId() - if text in ['Pan', 'Zoom']: - self.AddCheckTool(self.wx_ids[text], _load_bitmap(image_file + '.png'), - shortHelp=text, longHelp=tooltip_text) + if 'phoenix' in wx.PlatformInfo: + if text in ['Pan', 'Zoom']: + kind = wx.ITEM_CHECK + else: + kind = wx.ITEM_NORMAL + self.AddTool(self.wx_ids[text], label=text, + bitmap=_load_bitmap(image_file + '.png'), + bmpDisabled=wx.NullBitmap, + shortHelpString=text, + longHelpString=tooltip_text, + kind=kind) else: - self.AddSimpleTool(self.wx_ids[text], _load_bitmap(image_file + '.png'), - text, tooltip_text) + if text in ['Pan', 'Zoom']: + self.AddCheckTool(self.wx_ids[text], _load_bitmap(image_file + '.png'), + shortHelp=text, longHelp=tooltip_text) + else: + self.AddSimpleTool(self.wx_ids[text], _load_bitmap(image_file + '.png'), + text, tooltip_text) + bind(self, wx.EVT_TOOL, getattr(self, callback), id=self.wx_ids[text]) self.Realize() @@ -1700,7 +1726,10 @@ def save_figure(self, *args): error_msg_wx(str(e)) def set_cursor(self, cursor): - cursor =wx.StockCursor(cursord[cursor]) + if 'phoenix' in wx.PlatformInfo: + cursor = wx.Cursor(cursord[cursor]) + else: + cursor = wx.StockCursor(cursord[cursor]) self.canvas.SetCursor( cursor ) def release(self, event): @@ -1737,7 +1766,8 @@ def draw_rubberband(self, event, x0, y0, x1, y1): dc.ResetBoundingBox() - dc.BeginDrawing() + if not 'phoenix' in wx.PlatformInfo: + dc.BeginDrawing() height = self.canvas.figure.bbox.height y1 = height - y1 y0 = height - y0 @@ -1754,7 +1784,8 @@ def draw_rubberband(self, event, x0, y0, x1, y1): else: dc.DrawRectangle(*lastrect) #erase last self.lastrect = rect dc.DrawRectangle(*rect) - dc.EndDrawing() + if not 'phoenix' in wx.PlatformInfo: + dc.EndDrawing() def set_status_bar(self, statbar): self.statbar = statbar diff --git a/lib/matplotlib/backends/backend_wxagg.py b/lib/matplotlib/backends/backend_wxagg.py index 5dd01030560b..c3dbea202b54 100644 --- a/lib/matplotlib/backends/backend_wxagg.py +++ b/lib/matplotlib/backends/backend_wxagg.py @@ -136,7 +136,10 @@ def _convert_agg_to_wx_image(agg, bbox): """ if bbox is None: # agg => rgb -> image - image = wx.EmptyImage(int(agg.width), int(agg.height)) + if 'phoenix' in wx.PlatformInfo: + image = wx.Image(int(agg.width), int(agg.height)) + else: + image = wx.EmptyImage(int(agg.width), int(agg.height)) image.SetData(agg.tostring_rgb()) return image else: @@ -153,8 +156,12 @@ def _convert_agg_to_wx_bitmap(agg, bbox): """ if bbox is None: # agg => rgba buffer -> bitmap - return wx.BitmapFromBufferRGBA(int(agg.width), int(agg.height), - agg.buffer_rgba()) + if 'phoenix' in wx.PlatformInfo: + return wx.Bitmap.FromBufferRGBA(int(agg.width), int(agg.height), + agg.buffer_rgba()) + else: + return wx.BitmapFromBufferRGBA(int(agg.width), int(agg.height), + agg.buffer_rgba()) else: # agg => rgba buffer -> bitmap => clipped bitmap return _WX28_clipped_agg_as_bitmap(agg, bbox) @@ -170,12 +177,19 @@ def _WX28_clipped_agg_as_bitmap(agg, bbox): r = l + width t = b + height - srcBmp = wx.BitmapFromBufferRGBA(int(agg.width), int(agg.height), - agg.buffer_rgba()) + if 'phoenix' in wx.PlatformInfo: + srcBmp = wx.Bitmap.FromBufferRGBA(int(agg.width), int(agg.height), + agg.buffer_rgba()) + else: + srcBmp = wx.BitmapFromBufferRGBA(int(agg.width), int(agg.height), + agg.buffer_rgba()) srcDC = wx.MemoryDC() srcDC.SelectObject(srcBmp) - destBmp = wx.EmptyBitmap(int(width), int(height)) + if 'phoenix' in wx.PlatformInfo: + destBmp = wx.Bitmap(int(width), int(height)) + else: + destBmp = wx.EmptyBitmap(int(width), int(height)) destDC = wx.MemoryDC() destDC.SelectObject(destBmp) From 556ff866ca2eebf24f9b8dde36bd43656400dbba Mon Sep 17 00:00:00 2001 From: Werner F Bruhin Date: Tue, 11 Feb 2014 16:49:40 +0100 Subject: [PATCH 04/90] - get wx examples compatible with wxPython Phoenix --- examples/user_interfaces/embedding_in_wx2.py | 26 +++++++++-- examples/user_interfaces/embedding_in_wx3.py | 29 ++++++++++-- examples/user_interfaces/embedding_in_wx4.py | 33 ++++++++++---- examples/user_interfaces/embedding_in_wx5.py | 31 ++++++++++--- examples/user_interfaces/fourier_demo_wx.py | 48 +++++++++++++------- examples/user_interfaces/wxcursor_demo.py | 26 +++++++++-- 6 files changed, 149 insertions(+), 44 deletions(-) diff --git a/examples/user_interfaces/embedding_in_wx2.py b/examples/user_interfaces/embedding_in_wx2.py index 2fe9d09376b6..4d5e4ec1554b 100644 --- a/examples/user_interfaces/embedding_in_wx2.py +++ b/examples/user_interfaces/embedding_in_wx2.py @@ -6,7 +6,10 @@ # Used to guarantee to use at least Wx2.8 import wxversion -wxversion.ensureMinimal('2.8') +#wxversion.ensureMinimal('2.8') +wxversion.select('2.8') +#wxversion.select('2.9.5') # 2.9.x classic +#wxversion.select('3.0.1-msw-phoenix', optionsRequired=True) # 3.x Phoenix from numpy import arange, sin, pi @@ -25,6 +28,10 @@ from matplotlib.figure import Figure import wx +print wx.VERSION_STRING +print wx.PlatformInfo +print matplotlib.__version__ + class CanvasFrame(wx.Frame): @@ -32,7 +39,10 @@ def __init__(self): wx.Frame.__init__(self,None,-1, 'CanvasFrame',size=(550,350)) - self.SetBackgroundColour(wx.NamedColour("WHITE")) + if 'phoenix' in wx.PlatformInfo: + self.SetBackgroundColour(wx.Colour("WHITE")) + else: + self.SetBackgroundColour(wx.NamedColour("WHITE")) self.figure = Figure() self.axes = self.figure.add_subplot(111) @@ -48,7 +58,11 @@ def __init__(self): self.Fit() self.add_toolbar() # comment this out for no toolbar + self.Bind(wx.EVT_PAINT, self.OnPaint) + def OnPaint(self, event): + self.canvas.draw() + event.Skip() def add_toolbar(self): self.toolbar = NavigationToolbar2Wx(self.canvas) @@ -61,8 +75,12 @@ def add_toolbar(self): else: # On Windows platform, default window size is incorrect, so set # toolbar width to figure width. - tw, th = self.toolbar.GetSizeTuple() - fw, fh = self.canvas.GetSizeTuple() + if 'phoenix' in wx.PlatformInfo: + tw, th = self.toolbar.GetSize() + fw, fh = self.canvas.GetSize() + else: + tw, th = self.toolbar.GetSizeTuple() + fw, fh = self.canvas.GetSizeTuple() # By adding toolbar in sizer, we are able to put it at the bottom # of the frame - so appearance is closer to GTK version. # As noted above, doesn't work for Mac. diff --git a/examples/user_interfaces/embedding_in_wx3.py b/examples/user_interfaces/embedding_in_wx3.py index 931e043a5fd3..0f4060dd411d 100755 --- a/examples/user_interfaces/embedding_in_wx3.py +++ b/examples/user_interfaces/embedding_in_wx3.py @@ -23,7 +23,10 @@ # Used to guarantee to use at least Wx2.8 import wxversion -wxversion.ensureMinimal('2.8') +#wxversion.ensureMinimal('2.8') +#wxversion.select('2.8') +#wxversion.select('2.9.5') # 2.9.x classic +wxversion.select('3.0.1-msw-phoenix', optionsRequired=True) # 3.0 phoenix import sys, time, os, gc import matplotlib @@ -35,6 +38,9 @@ import numpy as np import wx + +print(wx.VERSION_STRING) + import wx.xrc as xrc ERR_TOL = 1e-5 # floating point slop for peak-detection @@ -61,6 +67,11 @@ def __init__(self, parent): sizer.Add(self.toolbar, 0, wx.GROW) self.SetSizer(sizer) self.Fit() + self.Bind(wx.EVT_PAINT, self.OnPaint) + + def OnPaint(self, event): + self.canvas.draw() + event.Skip() def init_plot_data(self): a = self.fig.add_subplot(111) @@ -132,14 +143,22 @@ def OnInit(self): # whiz button ------------------ whiz_button = xrc.XRCCTRL(self.frame,"whiz_button") - wx.EVT_BUTTON(whiz_button, whiz_button.GetId(), - self.plotpanel.OnWhiz) + + if 'phoenix' in wx.PlatformInfo: + whiz_button.Bind(wx.EVT_BUTTON, self.plotpanel.OnWhiz) + else: + wx.EVT_BUTTON(whiz_button, whiz_button.GetId(), + self.plotpanel.OnWhiz) # bang button ------------------ bang_button = xrc.XRCCTRL(self.frame,"bang_button") - wx.EVT_BUTTON(bang_button, bang_button.GetId(), - self.OnBang) + if 'phoenix' in wx.PlatformInfo: + bang_button.Bind(wx.EVT_BUTTON, self.OnBang) + + else: + wx.EVT_BUTTON(bang_button, bang_button.GetId(), + self.OnBang) # final setup ------------------ diff --git a/examples/user_interfaces/embedding_in_wx4.py b/examples/user_interfaces/embedding_in_wx4.py index 578be3b5da13..f982ea8a1321 100644 --- a/examples/user_interfaces/embedding_in_wx4.py +++ b/examples/user_interfaces/embedding_in_wx4.py @@ -6,7 +6,11 @@ # Used to guarantee to use at least Wx2.8 import wxversion -wxversion.ensureMinimal('2.8') +#wxversion.ensureMinimal('2.8') +wxversion.select('2.8') +#wxversion.select('2.9.5') # 2.9.x classic +#wxversion.select('3.0.1-msw-phoenix', optionsRequired=True) # 3.0 phoenix + from numpy import arange, sin, pi @@ -32,9 +36,15 @@ def __init__(self, canvas, cankill): # for simplicity I'm going to reuse a bitmap from wx, you'll # probably want to add your own. - self.AddSimpleTool(self.ON_CUSTOM, _load_bitmap('stock_left.xpm'), - 'Click me', 'Activate custom contol') - wx.EVT_TOOL(self, self.ON_CUSTOM, self._on_custom) + if 'phoenix' in wx.PlatformInfo: + self.AddTool(self.ON_CUSTOM, 'Click me', + _load_bitmap('stock_left.xpm'), + 'Activate custom contol') + self.Bind(wx.EVT_TOOL, self._on_custom, id=self.ON_CUSTOM) + else: + self.AddSimpleTool(self.ON_CUSTOM, _load_bitmap('stock_left.xpm'), + 'Click me', 'Activate custom contol') + wx.EVT_TOOL(self, self.ON_CUSTOM, self._on_custom) def _on_custom(self, evt): # add some text to the axes in a random location in axes (0,1) @@ -61,7 +71,10 @@ def __init__(self): wx.Frame.__init__(self,None,-1, 'CanvasFrame',size=(550,350)) - self.SetBackgroundColour(wx.NamedColour("WHITE")) + if 'phoenix' in wx.PlatformInfo: + self.SetBackgroundColour(wx.Colour("WHITE")) + else: + self.SetBackgroundColour(wx.NamedColour("WHITE")) self.figure = Figure(figsize=(5,4), dpi=100) self.axes = self.figure.add_subplot(111) @@ -75,7 +88,7 @@ def __init__(self): self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add(self.canvas, 1, wx.TOP | wx.LEFT | wx.EXPAND) # Capture the paint message - wx.EVT_PAINT(self, self.OnPaint) + self.Bind(wx.EVT_PAINT, self.OnPaint) self.toolbar = MyNavigationToolbar(self.canvas, True) self.toolbar.Realize() @@ -87,8 +100,12 @@ def __init__(self): else: # On Windows platform, default window size is incorrect, so set # toolbar width to figure width. - tw, th = self.toolbar.GetSizeTuple() - fw, fh = self.canvas.GetSizeTuple() + if 'phoenix' in wx.PlatformInfo: + tw, th = self.toolbar.GetSize() + fw, fh = self.canvas.GetSize() + else: + tw, th = self.toolbar.GetSizeTuple() + fw, fh = self.canvas.GetSizeTuple() # By adding toolbar in sizer, we are able to put it at the bottom # of the frame - so appearance is closer to GTK version. # As noted above, doesn't work for Mac. diff --git a/examples/user_interfaces/embedding_in_wx5.py b/examples/user_interfaces/embedding_in_wx5.py index fd3969aca881..ec4f7ebff7e0 100644 --- a/examples/user_interfaces/embedding_in_wx5.py +++ b/examples/user_interfaces/embedding_in_wx5.py @@ -1,9 +1,19 @@ # Used to guarantee to use at least Wx2.8 import wxversion -wxversion.ensureMinimal('2.8') +#wxversion.ensureMinimal('2.8') +wxversion.select('2.8') +#wxversion.select('2.9.5') # 2.9.x classic +#wxversion.select('3.0.1-msw-phoenix', optionsRequired=True) # 3.0 phoenix + import wx -import wx.aui +print wx.VERSION_STRING + +if 'phoenix' in wx.PlatformInfo: + import wx.lib.agw.aui as aui +else: + import wx.aui as aui + import matplotlib as mpl from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas from matplotlib.backends.backend_wxagg import NavigationToolbar2Wx as Toolbar @@ -20,23 +30,30 @@ def __init__(self, parent, id = -1, dpi = None, **kwargs): sizer.Add(self.canvas,1,wx.EXPAND) sizer.Add(self.toolbar, 0 , wx.LEFT | wx.EXPAND) self.SetSizer(sizer) + self.Bind(wx.EVT_PAINT, self.OnPaint) + + def OnPaint(self, event): + self.canvas.draw() + event.Skip() + class PlotNotebook(wx.Panel): def __init__(self, parent, id = -1): wx.Panel.__init__(self, parent, id=id) - self.nb = wx.aui.AuiNotebook(self) + self.nb = aui.AuiNotebook(self) sizer = wx.BoxSizer() sizer.Add(self.nb, 1, wx.EXPAND) self.SetSizer(sizer) def add(self,name="plot"): - page = Plot(self.nb) - self.nb.AddPage(page,name) - return page.figure + page = Plot(self.nb) + self.nb.AddPage(page,name) + return page.figure def demo(): - app = wx.PySimpleApp() + import wx.lib.mixins.inspection as wit + app = wit.InspectableApp() frame = wx.Frame(None,-1,'Plotter') plotter = PlotNotebook(frame) axes1 = plotter.add('figure 1').gca() diff --git a/examples/user_interfaces/fourier_demo_wx.py b/examples/user_interfaces/fourier_demo_wx.py index aa52809ff1c0..1d2ee7971322 100644 --- a/examples/user_interfaces/fourier_demo_wx.py +++ b/examples/user_interfaces/fourier_demo_wx.py @@ -1,6 +1,15 @@ import numpy as np -import wx +# Used to guarantee to use at least Wx2.8 +import wxversion +#wxversion.ensureMinimal('2.8') +wxversion.select('2.8') +#wxversion.select('2.9.5') # 2.9.x classic +#wxversion.select('3.0.1-msw-phoenix', optionsRequired=True) # 3.0 phoenix + + +import wx +print wx.VERSION_STRING import matplotlib matplotlib.interactive(False) matplotlib.use('WXAgg') @@ -11,7 +20,7 @@ class Knob: """ - Knob - simple class with a "setKnob" method. + Knob - simple class with a "setKnob" method. A Knob instance is attached to a Param instance, e.g., param.attach(knob) Base class is for documentation purposes. """ @@ -24,8 +33,8 @@ class Param: The idea of the "Param" class is that some parameter in the GUI may have several knobs that both control it and reflect the parameter's state, e.g. a slider, text, and dragging can all change the value of the frequency in - the waveform of this example. - The class allows a cleaner way to update/"feedback" to the other knobs when + the waveform of this example. + The class allows a cleaner way to update/"feedback" to the other knobs when one is being changed. Also, this class handles min/max constraints for all the knobs. Idea - knob list - in "set" method, knob object is passed as well @@ -39,10 +48,10 @@ def __init__(self, initialValue=None, minimum=0., maximum=1.): raise ValueError('illegal initial value') self.value = initialValue self.knobs = [] - + def attach(self, knob): self.knobs += [knob] - + def set(self, value, knob=None): self.value = value self.value = self.constrain(value) @@ -64,9 +73,10 @@ def __init__(self, parent, label, param): self.sliderLabel = wx.StaticText(parent, label=label) self.sliderText = wx.TextCtrl(parent, -1, style=wx.TE_PROCESS_ENTER) self.slider = wx.Slider(parent, -1) - self.slider.SetMax(param.maximum*1000) + #self.slider.SetMax(param.maximum*1000) + self.slider.SetRange(0, param.maximum*1000) self.setKnob(param.value) - + sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.sliderLabel, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=2) sizer.Add(self.sliderText, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=2) @@ -82,11 +92,11 @@ def __init__(self, parent, label, param): def sliderHandler(self, evt): value = evt.GetInt() / 1000. self.param.set(value) - + def sliderTextHandler(self, evt): value = float(self.sliderText.GetValue()) self.param.set(value) - + def setKnob(self, value): self.sliderText.SetValue('%g'%value) self.slider.SetValue(value*1000) @@ -109,7 +119,7 @@ def __init__(self, *args, **kwargs): sizer.Add(self.amplitudeSliderGroup.sizer, 0, \ wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=5) self.SetSizer(sizer) - + class FourierDemoWindow(wx.Window, Knob): def __init__(self, *args, **kwargs): @@ -125,7 +135,7 @@ def __init__(self, *args, **kwargs): self.f0 = Param(2., minimum=0., maximum=6.) self.A = Param(1., minimum=0.01, maximum=2.) self.draw() - + # Not sure I like having two params attached to the same Knob, # but that is what we have here... it works but feels kludgy - # although maybe it's not too bad since the knob changes both params @@ -133,10 +143,16 @@ def __init__(self, *args, **kwargs): self.f0.attach(self) self.A.attach(self) self.Bind(wx.EVT_SIZE, self.sizeHandler) - + + self.Bind(wx.EVT_PAINT, self.OnPaint) + + def OnPaint(self, event): + self.canvas.draw() + event.Skip() + def sizeHandler(self, *args, **kwargs): self.canvas.SetSize(self.GetSize()) - + def mouseDown(self, evt): if self.lines[0] in self.figure.hitlist(evt): self.state = 'frequency' @@ -159,7 +175,7 @@ def mouseMotion(self, evt): elif self.state == 'time': if (x-x0)/x0 != -1.: self.f0.set(1./(1./f0Init+(1./f0Init*(x-x0)/x0))) - + def mouseUp(self, evt): self.state = '' @@ -209,6 +225,6 @@ def OnInit(self): self.frame1 = FourierDemoFrame(parent=None, title="Fourier Demo", size=(640, 480)) self.frame1.Show() return True - + app = App() app.MainLoop() diff --git a/examples/user_interfaces/wxcursor_demo.py b/examples/user_interfaces/wxcursor_demo.py index 4d610ffc44d8..93be5f001364 100644 --- a/examples/user_interfaces/wxcursor_demo.py +++ b/examples/user_interfaces/wxcursor_demo.py @@ -1,6 +1,12 @@ """ Example to draw a cursor and report the data coords in wx """ +# Used to guarantee to use at least Wx2.8 +import wxversion +wxversion.ensureMinimal('2.8') +#wxversion.select('2.8') +#wxversion.select('2.9.5') # 2.9.x classic +#wxversion.select('2.9.6-msw-phoenix') # 2.9.x phoenix import matplotlib matplotlib.use('WXAgg') @@ -11,14 +17,17 @@ from numpy import arange, sin, pi import wx +print wx.VERSION_STRING class CanvasFrame(wx.Frame): def __init__(self, ): wx.Frame.__init__(self,None,-1, 'CanvasFrame',size=(550,350)) - - self.SetBackgroundColour(wx.NamedColour("WHITE")) + if 'phoenix' in wx.PlatformInfo: + self.SetBackgroundColour(wx.Colour("WHITE")) + else: + self.SetBackgroundColour(wx.NamedColour("WHITE")) self.figure = Figure() self.axes = self.figure.add_subplot(111) @@ -41,14 +50,23 @@ def __init__(self, ): self.statusBar = wx.StatusBar(self, -1) self.statusBar.SetFieldsCount(1) + self.statusBar.SetStatusWidths([-1]) self.SetStatusBar(self.statusBar) self.toolbar = NavigationToolbar2Wx(self.figure_canvas) self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) self.toolbar.Show() - + self.Bind(wx.EVT_PAINT, self.OnPaint) + + def OnPaint(self, event): + self.figure_canvas.draw() + event.Skip() + def ChangeCursor(self, event): - self.figure_canvas.SetCursor(wx.StockCursor(wx.CURSOR_BULLSEYE)) + if 'phoenix' in wx.PlatformInfo: + self.figure_canvas.SetCursor(wx.Cursor(wx.CURSOR_BULLSEYE)) + else: + self.figure_canvas.SetCursor(wx.StockCursor(wx.CURSOR_BULLSEYE)) def UpdateStatusBar(self, event): if event.inaxes: From 7b9fef5b0a51dbcc94fc90c2812a3af07f94e1b6 Mon Sep 17 00:00:00 2001 From: Katy Huff Date: Sat, 12 Jul 2014 10:34:14 -0500 Subject: [PATCH 05/90] replaces epd and pythonxy with anaconda and canopy. Also, added some notions in the readme.osx --- INSTALL | 22 +++++++++++----------- README.osx | 25 ++++++++++++++++++------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/INSTALL b/INSTALL index 8e165006da22..05cb37b9a591 100644 --- a/INSTALL +++ b/INSTALL @@ -11,17 +11,17 @@ depends on what operating system you are using, what you already have installed, and how you want to use it. To avoid wading through all the details (and potential complications) on this page, the easiest thing for you to do is use one of the pre-packaged python -distributions that already provide matplotlib built-in. The Enthought -Python Distribution `(EPD) -`_ for Windows, OS X or -Redhat is an excellent choice that "just works" out of the box. -Another excellent alternative for Windows users is `Python (x, y) -`_ which tends to be updated a bit more -frequently. Both of these packages include matplotlib and pylab, and -*lots* of other useful tools. matplotlib is also packaged for almost -every major Linux distribution. So if you are on Linux, your package -manager will probably provide matplotlib prebuilt. - +distributions that already provide matplotlib built-in. The +Continuum.io distribution `(Anaconda) +`_ or the Enthought distribution +`(Canopy) `_ for Windows, OS X, or +Redhat is an excellent choice that "just works" out of the box. Another +excellent alternative for Windows users is `Python (x, y) +`_ which tends to be updated a bit more frequently. +Both of these packages include matplotlib and pylab, and *lots* of other useful +tools. matplotlib is also packaged for almost every major Linux distribution. +So if you are on Linux, your package manager will probably provide matplotlib +prebuilt. Manually installing pre-built packages ====================================== diff --git a/README.osx b/README.osx index c2ca0a59afce..b12f8569b09d 100644 --- a/README.osx +++ b/README.osx @@ -1,11 +1,18 @@ -Building mpl on OSX has proven to be a nightmare because of all the +Building mpl on OSX is sometimes a nightmare because of all the different types of zlib, png and freetype that may be on your system. -The recommended and supported way to build is to use a third-party -package manager to install the required dependencies, and then -install matplotlib from source using the setup.py script. Two widely -used package managers are homebrew and MacPorts. The following -example illustrates how to install libpng and freetype using -homebrew. + +For developers who want to build matplotlib from source, the recommended and +supported way to build is to use a third-party package manager to install the +required dependencies, and then install matplotlib from source using the +setup.py script. Three widely used package managers are conda, homebrew, and +MacPorts. The following example illustrates how to install libpng and freetype +using conda: + +Example usage:: + + conda install libpng freetype + +If you are using homebrew, execute the following instead: Example usage:: @@ -22,3 +29,7 @@ To install matplotlib from source, execute: Example usage:: python setup.py install + + +Note that your environment is somewhat important. Some conda users have found +that their PYTHONPATH must include /path/to/anaconda/ From 71080990a80056f863f689c7666dfa37d4bc54f1 Mon Sep 17 00:00:00 2001 From: Katy Huff Date: Sat, 12 Jul 2014 10:38:26 -0500 Subject: [PATCH 06/90] adds dyld_fallback_library_path --- README.osx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.osx b/README.osx index b12f8569b09d..0cf5a39f2dc2 100644 --- a/README.osx +++ b/README.osx @@ -31,5 +31,7 @@ Example usage:: python setup.py install -Note that your environment is somewhat important. Some conda users have found -that their PYTHONPATH must include /path/to/anaconda/ +Note that your environment is somewhat important. Some conda users have +found that their PYTHONPATH must include +/path/to/anaconda/.../site-packages and their DYLD_FALLBACK_LIBRARY_PATH +must include /path/to/anaconda/lib. From a79cfc61bd7af507519729d0840a01bb3806bb50 Mon Sep 17 00:00:00 2001 From: Katy Huff Date: Sat, 12 Jul 2014 10:41:18 -0500 Subject: [PATCH 07/90] removing pythonxy --- INSTALL | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/INSTALL b/INSTALL index 05cb37b9a591..4fe37bc82796 100644 --- a/INSTALL +++ b/INSTALL @@ -15,9 +15,7 @@ distributions that already provide matplotlib built-in. The Continuum.io distribution `(Anaconda) `_ or the Enthought distribution `(Canopy) `_ for Windows, OS X, or -Redhat is an excellent choice that "just works" out of the box. Another -excellent alternative for Windows users is `Python (x, y) -`_ which tends to be updated a bit more frequently. +Redhat is an excellent choice that "just works" out of the box. Both of these packages include matplotlib and pylab, and *lots* of other useful tools. matplotlib is also packaged for almost every major Linux distribution. So if you are on Linux, your package manager will probably provide matplotlib From d847a85840f4e736f721b7ab6d4e36a30da36aee Mon Sep 17 00:00:00 2001 From: Katy Huff Date: Sat, 12 Jul 2014 10:55:36 -0500 Subject: [PATCH 08/90] reformatting for line length --- INSTALL | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/INSTALL b/INSTALL index 4fe37bc82796..6403037f6f1e 100644 --- a/INSTALL +++ b/INSTALL @@ -11,15 +11,16 @@ depends on what operating system you are using, what you already have installed, and how you want to use it. To avoid wading through all the details (and potential complications) on this page, the easiest thing for you to do is use one of the pre-packaged python -distributions that already provide matplotlib built-in. The -Continuum.io distribution `(Anaconda) -`_ or the Enthought distribution -`(Canopy) `_ for Windows, OS X, or -Redhat is an excellent choice that "just works" out of the box. -Both of these packages include matplotlib and pylab, and *lots* of other useful -tools. matplotlib is also packaged for almost every major Linux distribution. -So if you are on Linux, your package manager will probably provide matplotlib -prebuilt. +distributions that already provide matplotlib built-in. The Continuum.io +Python distribution `(Anaconda) +`_ for Windows, OS X, or Redhat +is an excellent choice that "just works" out of the box. +The Enthought distribution `(Canopy) +`_ is also a good a good choice +for any platform. Both of these packages include matplotlib and pylab, and +*lots* of other useful tools. matplotlib is also packaged for almost +every major Linux distribution. So if you are on Linux, your package +manager will probably provide matplotlib prebuilt. Manually installing pre-built packages ====================================== From 7e2fdd2a47d79757682927e2ccd50b6b1359f6ac Mon Sep 17 00:00:00 2001 From: Katy Huff Date: Sat, 12 Jul 2014 11:05:25 -0500 Subject: [PATCH 09/90] redhat->linux --- INSTALL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL b/INSTALL index 6403037f6f1e..45269af8c142 100644 --- a/INSTALL +++ b/INSTALL @@ -13,7 +13,7 @@ the details (and potential complications) on this page, the easiest thing for you to do is use one of the pre-packaged python distributions that already provide matplotlib built-in. The Continuum.io Python distribution `(Anaconda) -`_ for Windows, OS X, or Redhat +`_ for Windows, OS X, or Linux is an excellent choice that "just works" out of the box. The Enthought distribution `(Canopy) `_ is also a good a good choice From 156ff59fa3c57fa1221f974df730f3d260d0e123 Mon Sep 17 00:00:00 2001 From: Katy Huff Date: Sat, 12 Jul 2014 11:06:22 -0500 Subject: [PATCH 10/90] drops pylab reference --- INSTALL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL b/INSTALL index 45269af8c142..36f79bc8ada9 100644 --- a/INSTALL +++ b/INSTALL @@ -17,7 +17,7 @@ Python distribution `(Anaconda) is an excellent choice that "just works" out of the box. The Enthought distribution `(Canopy) `_ is also a good a good choice -for any platform. Both of these packages include matplotlib and pylab, and +for any platform. Both of these packages include matplotlib and *lots* of other useful tools. matplotlib is also packaged for almost every major Linux distribution. So if you are on Linux, your package manager will probably provide matplotlib prebuilt. From a68e33e9c42664ff468f588bbb0753a2741b6119 Mon Sep 17 00:00:00 2001 From: Katy Huff Date: Sat, 12 Jul 2014 11:07:00 -0500 Subject: [PATCH 11/90] getting rid of extra space --- INSTALL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL b/INSTALL index 36f79bc8ada9..2d0cb5655c88 100644 --- a/INSTALL +++ b/INSTALL @@ -19,7 +19,7 @@ The Enthought distribution `(Canopy) `_ is also a good a good choice for any platform. Both of these packages include matplotlib and *lots* of other useful tools. matplotlib is also packaged for almost -every major Linux distribution. So if you are on Linux, your package +every major Linux distribution. So if you are on Linux, your package manager will probably provide matplotlib prebuilt. Manually installing pre-built packages From 9bd9c6f70a4ff2945d9490ddc299d9a730c9e747 Mon Sep 17 00:00:00 2001 From: Katy Huff Date: Sat, 12 Jul 2014 14:46:56 -0500 Subject: [PATCH 12/90] both are good distributions --- INSTALL | 9 ++++----- README.osx | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/INSTALL b/INSTALL index 2d0cb5655c88..e7fcbc7bb648 100644 --- a/INSTALL +++ b/INSTALL @@ -13,11 +13,10 @@ the details (and potential complications) on this page, the easiest thing for you to do is use one of the pre-packaged python distributions that already provide matplotlib built-in. The Continuum.io Python distribution `(Anaconda) -`_ for Windows, OS X, or Linux -is an excellent choice that "just works" out of the box. -The Enthought distribution `(Canopy) -`_ is also a good a good choice -for any platform. Both of these packages include matplotlib and +`_ and the Enthought +distribution `(Canopy) `_ +are both excellent choices that "just work" out of the box for any +platform. Both of these packages include matplotlib and *lots* of other useful tools. matplotlib is also packaged for almost every major Linux distribution. So if you are on Linux, your package manager will probably provide matplotlib prebuilt. diff --git a/README.osx b/README.osx index 0cf5a39f2dc2..0d528d28122a 100644 --- a/README.osx +++ b/README.osx @@ -4,7 +4,7 @@ different types of zlib, png and freetype that may be on your system. For developers who want to build matplotlib from source, the recommended and supported way to build is to use a third-party package manager to install the required dependencies, and then install matplotlib from source using the -setup.py script. Three widely used package managers are conda, homebrew, and +setup.py script. Three widely used package managers are homebrew, and MacPorts. The following example illustrates how to install libpng and freetype using conda: From 1b3243c55545f31be5970c44a3558d8855c50b07 Mon Sep 17 00:00:00 2001 From: Katy Huff Date: Sat, 12 Jul 2014 14:48:14 -0500 Subject: [PATCH 13/90] conda libpng and freetype don't help --- README.osx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/README.osx b/README.osx index 0d528d28122a..f1756813631f 100644 --- a/README.osx +++ b/README.osx @@ -6,13 +6,7 @@ supported way to build is to use a third-party package manager to install the required dependencies, and then install matplotlib from source using the setup.py script. Three widely used package managers are homebrew, and MacPorts. The following example illustrates how to install libpng and freetype -using conda: - -Example usage:: - - conda install libpng freetype - -If you are using homebrew, execute the following instead: +using brew: Example usage:: @@ -32,6 +26,6 @@ Example usage:: Note that your environment is somewhat important. Some conda users have -found that their PYTHONPATH must include +found that, to run the tests, their PYTHONPATH must include /path/to/anaconda/.../site-packages and their DYLD_FALLBACK_LIBRARY_PATH must include /path/to/anaconda/lib. From 9dedc0f2c3bd201f32daed00e4c9e88df6050468 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 30 Jun 2014 00:08:50 -0400 Subject: [PATCH 14/90] DOC : change how OO/pyplot/pylab is addressed I assume this will be controversial Addresses #3115 --- doc/faq/usage_faq.rst | 127 ++++++++++++++++++++++++++++-------------- 1 file changed, 86 insertions(+), 41 deletions(-) diff --git a/doc/faq/usage_faq.rst b/doc/faq/usage_faq.rst index 749151f9f3a9..47039121760c 100644 --- a/doc/faq/usage_faq.rst +++ b/doc/faq/usage_faq.rst @@ -55,31 +55,50 @@ Matplotlib is the whole package; :mod:`pylab` is a module in matplotlib that gets installed alongside :mod:`matplotlib`; and :mod:`matplotlib.pyplot` is a module in matplotlib. -Pyplot provides the state-machine interface to the underlying plotting +Pyplot provides the state-machine interface to the underlying OO plotting library in matplotlib. This means that figures and axes are implicitly and automatically created to achieve the desired plot. For example, calling ``plot`` from pyplot will automatically create the necessary figure and axes to achieve the desired plot. Setting a title will then automatically set that title to the current axes object:: - import matplotlib.pyplot as plt - plt.plot(range(10), range(10)) - plt.title("Simple Plot") - plt.show() + .. sourcecode:: ipython + + In [20]: %matplotlib + Using matplotlib backend: Qt4Agg + + In [15]: import matplotlib.pyplot as plt + + In [16]: plt.plot(range(10), range(10)) + Out[16]: [] + + In [17]: plt.title("Simple Plot") + Out[17]: + +This is very convenient for interactive use, however +because the commands have side-effects (altering the global state) +using many :mod:`matplotlib.pyplot` commands in scripts or functions +can lead to unexpected and difficult to track down bugs. -Pylab combines the pyplot functionality (for plotting) with the numpy -functionality (for mathematics and for working with arrays) -in a single namespace, making that namespace -(or environment) even more MATLAB-like. -For example, one can call the `sin` and `cos` functions just like -you could in MATLAB, as well as having all the features of pyplot. +Pylab is a convenience module that imports pyplot (for +plotting) and numpy functionality (for mathematics and for +working with arrays) in a single namespace. You can than bulk import +from pylab:: -The pyplot interface is generally preferred for non-interactive plotting -(i.e., scripting). The pylab interface is convenient for interactive -calculations and plotting, as it minimizes typing. Note that this is -what you get if you use the *ipython* shell with the *-pylab* option, -which imports everything from pylab and makes plotting fully interactive. + .. sourcecode:: ipython + + In [1]: from pylab import * + +to get an even more MATLAB-like environment. For example, one can +call the `sin` and `cos` functions just like you could in MATLAB, as +well as having all the features of pyplot. The pylab interface is +convenient for interactive calculations and plotting, as it minimizes +typing. This is not recommended to use pylab in scripts for the same reasons +bulk importing is discouraged in general. + +For non-interactive use it is suggested to use pyplot to create the +figures and then the OO interface for plotting. .. _coding_styles: @@ -106,18 +125,7 @@ scripts will typically be:: import numpy as np Then one calls, for example, np.arange, np.zeros, np.pi, plt.figure, -plt.plot, plt.show, etc. So, a simple example in this style would be:: - - import matplotlib.pyplot as plt - import numpy as np - x = np.arange(0, 10, 0.2) - y = np.sin(x) - plt.plot(x, y) - plt.show() - -Note that this example used pyplot's state-machine to -automatically and implicitly create a figure and an axes. For full -control of your plots and more advanced usage, use the pyplot interface +plt.plot, plt.show, etc. Use the pyplot interface for creating figures, and then use the object methods for the rest:: import matplotlib.pyplot as plt @@ -129,22 +137,59 @@ for creating figures, and then use the object methods for the rest:: ax.plot(x, y) plt.show() -Next, the same example using a pure MATLAB-style:: +So, why all the extra typing instead of the MATLAB-style (which relies +on global state and a flat namespace)? For very simple things like +this example, the only advantage is academic: the wordier styles are +more explicit, more clear as to where things come from and what is +going on. For more complicated applications, this explicitness and +clarity becomes increasingly valuable, and the richer and more +complete object-oriented interface will likely make the program easier +to write and maintain. + +Typically one finds them selves making the same plots over and over +again, but with different data sets, which leads to needing to write +specialized functions to do the plotting. The recommended function +signature is something like: :: + + def my_plotter(ax, data1, data2, param_dict): + """ + A helper function to make a graph + + Parameters + ---------- + ax : Axes + The axes to draw to + + data1 : array + The x data + + data2 : array + The y data + + param_dict : dict + Dictionary of kwargs to pass to ax.plot + + Returns + ------- + out : list + list of artists added + """ + out = ax.plot(data1, data2, **param_dict) + return out + +which you would then use as:: + + fig, ax = plt.subplots(1, 1) + my_plotter(ax, data1, data2, {'marker':'x'}) - from pylab import * - x = arange(0, 10, 0.2) - y = sin(x) - plot(x, y) - show() +or if you wanted to have 2 sub-plots:: + fig, (ax1, ax2) = plt.subplots(1, 2) + my_plotter(ax1, data1, data2, {'marker':'x'}) + my_plotter(ax2, data3, data4, {'marker':'o'}) -So, why all the extra typing as one moves away from the pure -MATLAB-style? For very simple things like this example, the only -advantage is academic: the wordier styles are more explicit, more -clear as to where things come from and what is going on. For more -complicated applications, this explicitness and clarity becomes -increasingly valuable, and the richer and more complete object-oriented -interface will likely make the program easier to write and maintain. +Again, for these simple examples this style seems like overkill, however +once the graphs get slightly more complex it pays off. .. _what-is-a-backend: From a2b946145b5e9845d05e079d71f6ad6cc4359c6d Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 3 Jul 2014 22:40:23 -0400 Subject: [PATCH 15/90] DOC : grammer fixes and doc edits - removed from pylab import * - changed some wording --- doc/faq/usage_faq.rst | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/doc/faq/usage_faq.rst b/doc/faq/usage_faq.rst index 47039121760c..b7699281b84e 100644 --- a/doc/faq/usage_faq.rst +++ b/doc/faq/usage_faq.rst @@ -65,7 +65,7 @@ then automatically set that title to the current axes object:: .. sourcecode:: ipython - In [20]: %matplotlib + In [14]: %matplotlib Using matplotlib backend: Qt4Agg In [15]: import matplotlib.pyplot as plt @@ -81,24 +81,18 @@ because the commands have side-effects (altering the global state) using many :mod:`matplotlib.pyplot` commands in scripts or functions can lead to unexpected and difficult to track down bugs. -Pylab is a convenience module that imports pyplot (for -plotting) and numpy functionality (for mathematics and for -working with arrays) in a single namespace. You can than bulk import -from pylab:: +Pylab is a convenience module that imports pyplot (for plotting) and +numpy functionality (for mathematics and for working with arrays) in a +single namespace. You can than bulk import from pylab to get an even +more MATLAB-like environment. This seems convenient for interactive +calculations and plotting, as it (barely) minimizes typing, however it +is not recommended as it clobbers your namespace. As with :mod:`pyplot`, +it is not recommended to use :mod:`pylab` in scripts and bulk importing +:mod:`pylab` in scripts is discouraged as with all bulk importing. - .. sourcecode:: ipython - - In [1]: from pylab import * - -to get an even more MATLAB-like environment. For example, one can -call the `sin` and `cos` functions just like you could in MATLAB, as -well as having all the features of pyplot. The pylab interface is -convenient for interactive calculations and plotting, as it minimizes -typing. This is not recommended to use pylab in scripts for the same reasons -bulk importing is discouraged in general. - -For non-interactive use it is suggested to use pyplot to create the -figures and then the OO interface for plotting. +For non-interactive plotting it is suggested +to use pyplot to create the figures and then the OO interface for +plotting. .. _coding_styles: @@ -146,7 +140,7 @@ clarity becomes increasingly valuable, and the richer and more complete object-oriented interface will likely make the program easier to write and maintain. -Typically one finds them selves making the same plots over and over +Typically one finds oneself making the same plots over and over again, but with different data sets, which leads to needing to write specialized functions to do the plotting. The recommended function signature is something like: :: From adc9ec4df4412c414fb23f600e414c13110ddb58 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 12 Jul 2014 23:39:14 -0400 Subject: [PATCH 16/90] DOC : edits to usage_faq.rst --- doc/faq/usage_faq.rst | 63 ++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/doc/faq/usage_faq.rst b/doc/faq/usage_faq.rst index b7699281b84e..731c4c6d2d8e 100644 --- a/doc/faq/usage_faq.rst +++ b/doc/faq/usage_faq.rst @@ -48,47 +48,48 @@ completely, leaving a purely object-oriented approach. .. _pylab: -Matplotlib, pylab, and pyplot: how are they related? +Matplotlib, pyplot and pylab: how are they related? ==================================================== -Matplotlib is the whole package; :mod:`pylab` is a module in matplotlib -that gets installed alongside :mod:`matplotlib`; and :mod:`matplotlib.pyplot` -is a module in matplotlib. +Matplotlib is the whole package; :mod:`matplotlib.pyplot` +is a module in matplotlib; and :mod:`pylab` is a module +that gets installed alongside :mod:`matplotlib`. -Pyplot provides the state-machine interface to the underlying OO plotting -library in matplotlib. This means that figures and axes are implicitly -and automatically created to achieve the desired plot. For example, -calling ``plot`` from pyplot will automatically create the necessary -figure and axes to achieve the desired plot. Setting a title will -then automatically set that title to the current axes object:: +Pyplot provides the state-machine interface to the underlying +object-oriented plotting library. The state-machine implicitly and +automatically creates figures and axes to achieve the desired +plot. For example:: - .. sourcecode:: ipython + import matplotlib.pyplot as plt + import numpy as np - In [14]: %matplotlib - Using matplotlib backend: Qt4Agg + x = np.linspace(0, 2, 100) - In [15]: import matplotlib.pyplot as plt + plt.plot(x, x, label='linear') + plt.plot(x, x**2, label='quadratic') + plt.plot(x, x**3, label='cubic') - In [16]: plt.plot(range(10), range(10)) - Out[16]: [] + plt.xlabel('x label') + plt.ylabel('y label') - In [17]: plt.title("Simple Plot") - Out[17]: + plt.title("Simple Plot") -This is very convenient for interactive use, however -because the commands have side-effects (altering the global state) -using many :mod:`matplotlib.pyplot` commands in scripts or functions -can lead to unexpected and difficult to track down bugs. + plt.legend() -Pylab is a convenience module that imports pyplot (for plotting) and -numpy functionality (for mathematics and for working with arrays) in a -single namespace. You can than bulk import from pylab to get an even -more MATLAB-like environment. This seems convenient for interactive -calculations and plotting, as it (barely) minimizes typing, however it -is not recommended as it clobbers your namespace. As with :mod:`pyplot`, -it is not recommended to use :mod:`pylab` in scripts and bulk importing -:mod:`pylab` in scripts is discouraged as with all bulk importing. + plt.show() + +The first call to ``plt.plot`` will automatically create the necessary +figure and axes to achieve the desired plot. Subsequent calls to +``plt.plot`` re-use the current axes and each add another line. +Setting the title, legend, and axis labels also automatically use the +current axes and set the title, create the legend, and label the axis +respectively. + +:mod:`pylab` is a convenience module that bulk imports +:mod:`matplotlib.pyplot` (for plotting) and :mod:`numpy` +(for mathematics and working with arrays) in a single name space. +Although many examples use :mod:`pylab`, it is no longer recommended. For non-interactive plotting it is suggested to use pyplot to create the figures and then the OO interface for @@ -112,7 +113,7 @@ The only caveat is to avoid mixing the coding styles for your own code. Of the different styles, there are two that are officially supported. Therefore, these are the preferred ways to use matplotlib. -For the preferred pyplot style, the imports at the top of your +For the pyplot style, the imports at the top of your scripts will typically be:: import matplotlib.pyplot as plt From a52cd33057cf39e989a0f6e1b7ff029a7730e063 Mon Sep 17 00:00:00 2001 From: Katy Huff Date: Sun, 13 Jul 2014 10:22:13 -0700 Subject: [PATCH 17/90] Two --- README.osx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.osx b/README.osx index f1756813631f..12915d488bf3 100644 --- a/README.osx +++ b/README.osx @@ -4,7 +4,7 @@ different types of zlib, png and freetype that may be on your system. For developers who want to build matplotlib from source, the recommended and supported way to build is to use a third-party package manager to install the required dependencies, and then install matplotlib from source using the -setup.py script. Three widely used package managers are homebrew, and +setup.py script. Two widely used package managers are homebrew, and MacPorts. The following example illustrates how to install libpng and freetype using brew: From 40861f79de4942bd63ab19ff1494b0916ceed694 Mon Sep 17 00:00:00 2001 From: Katy Huff Date: Sun, 13 Jul 2014 10:26:12 -0700 Subject: [PATCH 18/90] moves libpng from optional to required --- INSTALL | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/INSTALL b/INSTALL index e7fcbc7bb648..1b7150f2cd98 100644 --- a/INSTALL +++ b/INSTALL @@ -196,6 +196,12 @@ six 1.3 or later Python 2/3 compatibility library. This is also a dependency of :term:`dateutil`. +libpng 1.2 (or later) + library for loading and saving :term:`PNG` files (`download + `__). libpng requires + zlib. If you are a Windows user, you can ignore this because we + build support into the matplotlib single-click installer + **Optional GUI frameworks** @@ -239,13 +245,6 @@ ImageMagick user, you can ignore this since we build support into the matplotlib single click installer. - -libpng 1.2 (or later) - library for loading and saving :term:`PNG` files (`download - `__). libpng requires - zlib. If you are a Windows user, you can ignore this because we - build support into the matplotlib single-click installer - **Required libraries that ship with matplotlib** :term:`agg` 2.4 From 386a088dbd7c09759aa7d8d11d78019f0e1cabf7 Mon Sep 17 00:00:00 2001 From: cgohlke Date: Sun, 13 Jul 2014 13:10:46 -0700 Subject: [PATCH 19/90] TST: do not remove files while they are open Fixes WindowsError: [Error 32] The process cannot access the file because it is being used by another process --- lib/matplotlib/tests/test_backend_pdf.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/tests/test_backend_pdf.py b/lib/matplotlib/tests/test_backend_pdf.py index a5fcb5b17e4b..323078cefab8 100644 --- a/lib/matplotlib/tests/test_backend_pdf.py +++ b/lib/matplotlib/tests/test_backend_pdf.py @@ -64,7 +64,7 @@ def test_multipage_keep_empty(): with PdfPages(tmp) as pdf: filename = pdf._file.fh.name assert os.path.exists(filename) - os.remove(filename) + os.remove(filename) # test if an empty pdf is deleting itself afterwards with keep_empty=False with NamedTemporaryFile(delete=False) as tmp: with PdfPages(tmp, keep_empty=False) as pdf: @@ -80,11 +80,11 @@ def test_multipage_keep_empty(): filename = pdf._file.fh.name pdf.savefig() assert os.path.exists(filename) - os.remove(filename) + os.remove(filename) # test that a non-empty pdf is left behind with keep_empty=False with NamedTemporaryFile(delete=False) as tmp: with PdfPages(tmp, keep_empty=False) as pdf: filename = pdf._file.fh.name pdf.savefig() assert os.path.exists(filename) - os.remove(filename) + os.remove(filename) From aec5a567391aaf2a6640f128356fca45bc798e7b Mon Sep 17 00:00:00 2001 From: cgohlke Date: Sun, 13 Jul 2014 13:24:03 -0700 Subject: [PATCH 20/90] BUG: do not remove file if passed in file object Fix WindowsError: [Error 32] The process cannot access the file because it is being used by another process --- lib/matplotlib/backends/backend_pdf.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 49350e4fdda3..109f1ef92124 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -2,7 +2,7 @@ """ A PDF matplotlib backend -Author: Jouni K Seppänen +Author: Jouni K Sepp�nen """ from __future__ import (absolute_import, division, print_function, unicode_literals) @@ -2382,7 +2382,8 @@ def close(self): PDF file. """ self._file.close() - if self.get_pagecount() == 0 and self.keep_empty is False: + if (self.get_pagecount() == 0 and not self.keep_empty + and not self._file.passed_in_file_object): os.remove(self._file.fh.name) self._file = None From 0a6ee424cdff64e909bfec21869a0daee1498af3 Mon Sep 17 00:00:00 2001 From: cgohlke Date: Sun, 13 Jul 2014 13:27:55 -0700 Subject: [PATCH 21/90] TST: On Windows open files can not be removed --- lib/matplotlib/tests/test_backend_pdf.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/tests/test_backend_pdf.py b/lib/matplotlib/tests/test_backend_pdf.py index 323078cefab8..0e693402ca16 100644 --- a/lib/matplotlib/tests/test_backend_pdf.py +++ b/lib/matplotlib/tests/test_backend_pdf.py @@ -66,10 +66,9 @@ def test_multipage_keep_empty(): assert os.path.exists(filename) os.remove(filename) # test if an empty pdf is deleting itself afterwards with keep_empty=False - with NamedTemporaryFile(delete=False) as tmp: - with PdfPages(tmp, keep_empty=False) as pdf: - filename = pdf._file.fh.name - assert not os.path.exists(filename) + with PdfPages(filename, keep_empty=False) as pdf: + pass + assert not os.path.exists(filename) ### test pdf files with content, they should never be deleted fig = plt.figure() ax = fig.add_subplot(111) From ac1a333b6b3a26c458a86e17f30f782dfece376f Mon Sep 17 00:00:00 2001 From: Matthew Brett Date: Mon, 14 Jul 2014 13:37:48 -0400 Subject: [PATCH 22/90] STY: remove unused imports in plot_directive --- lib/matplotlib/sphinxext/plot_directive.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/matplotlib/sphinxext/plot_directive.py b/lib/matplotlib/sphinxext/plot_directive.py index dc9736194354..31d1f3d8dbfb 100644 --- a/lib/matplotlib/sphinxext/plot_directive.py +++ b/lib/matplotlib/sphinxext/plot_directive.py @@ -133,14 +133,13 @@ import six from six.moves import xrange -import sys, os, glob, shutil, imp, warnings, io, re, textwrap +import sys, os, shutil, io, re, textwrap import traceback if not six.PY3: import cStringIO from docutils.parsers.rst import directives -from docutils import nodes from docutils.parsers.rst.directives.images import Image align = Image.align import sphinx From d160d6ca7254587f21d933b4a9eda3c8ee8a8236 Mon Sep 17 00:00:00 2001 From: Matthew Brett Date: Mon, 14 Jul 2014 13:39:04 -0400 Subject: [PATCH 23/90] STY: pep8 2 lines between functions pep8 for plot directive. --- lib/matplotlib/sphinxext/plot_directive.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/matplotlib/sphinxext/plot_directive.py b/lib/matplotlib/sphinxext/plot_directive.py index 31d1f3d8dbfb..d4cb7ed7e008 100644 --- a/lib/matplotlib/sphinxext/plot_directive.py +++ b/lib/matplotlib/sphinxext/plot_directive.py @@ -238,6 +238,7 @@ def plot_directive(name, arguments, options, content, lineno, return run(arguments, content, options, state_machine, state, lineno) plot_directive.__doc__ = __doc__ + def _option_boolean(arg): if not arg or not arg.strip(): # no argument given, assume used as a flag @@ -266,6 +267,7 @@ def _option_align(arg): return directives.choice(arg, ("top", "middle", "bottom", "left", "center", "right")) + def mark_plot_labels(app, document): """ To make plots referenceable, we need to move the reference from @@ -296,6 +298,7 @@ def mark_plot_labels(app, document): document.settings.env.docname, labelid, sectname break + def setup(app): setup.app = app setup.config = app.config @@ -343,6 +346,7 @@ def contains_doctest(text): m = r.search(text) return bool(m) + def unescape_doctest(text): """ Extract code from a piece of text, which contains either Python code @@ -363,6 +367,7 @@ def unescape_doctest(text): code += "\n" return code + def split_code_at_show(text): """ Split code at plt.show() @@ -385,6 +390,7 @@ def split_code_at_show(text): parts.append("\n".join(part)) return parts + def remove_coding(text): """ Remove the coding comment, which six.exec_ doesn't like. @@ -479,6 +485,7 @@ def filename(self, format): def filenames(self): return [self.filename(fmt) for fmt in self.formats] + def out_of_date(original, derived): """ Returns True if derivative is out-of-date wrt original, @@ -488,9 +495,11 @@ def out_of_date(original, derived): (os.path.exists(original) and os.stat(derived).st_mtime < os.stat(original).st_mtime)) + class PlotError(RuntimeError): pass + def run_code(code, code_path, ns=None, function_name=None): """ Import a Python module from a path, and run the function given by @@ -564,12 +573,14 @@ def _dummy_print(*arg, **kwarg): sys.stdout = stdout return ns + def clear_state(plot_rcparams, close=True): if close: plt.close('all') matplotlib.rc_file_defaults() matplotlib.rcParams.update(plot_rcparams) + def render_figures(code, code_path, output_dir, output_base, context, function_name, config, context_reset=False): """ @@ -681,6 +692,7 @@ def render_figures(code, code_path, output_dir, output_base, context, return results + def run(arguments, content, options, state_machine, state, lineno): # The user may provide a filename *or* Python code content, but not both if arguments and content: From 7f0a450c3ba9906cad9d2fbf10f24c411ce32f94 Mon Sep 17 00:00:00 2001 From: Matthew Brett Date: Mon, 14 Jul 2014 13:42:08 -0400 Subject: [PATCH 24/90] MNT: remove Python 2.7 copy of relpath Matplotlib now depends on Python 2.6, which has relpath --- lib/matplotlib/sphinxext/plot_directive.py | 62 +--------------------- 1 file changed, 1 insertion(+), 61 deletions(-) diff --git a/lib/matplotlib/sphinxext/plot_directive.py b/lib/matplotlib/sphinxext/plot_directive.py index d4cb7ed7e008..4d2519f755c9 100644 --- a/lib/matplotlib/sphinxext/plot_directive.py +++ b/lib/matplotlib/sphinxext/plot_directive.py @@ -134,6 +134,7 @@ from six.moves import xrange import sys, os, shutil, io, re, textwrap +from os.path import relpath import traceback if not six.PY3: @@ -168,67 +169,6 @@ def format_template(template, **kw): __version__ = 2 -#------------------------------------------------------------------------------ -# Relative pathnames -#------------------------------------------------------------------------------ - -# os.path.relpath is new in Python 2.6 -try: - from os.path import relpath -except ImportError: - # Copied from Python 2.7 - if 'posix' in sys.builtin_module_names: - def relpath(path, start=os.path.curdir): - """Return a relative version of a path""" - from os.path import sep, curdir, join, abspath, commonprefix, \ - pardir - - if not path: - raise ValueError("no path specified") - - start_list = abspath(start).split(sep) - path_list = abspath(path).split(sep) - - # Work out how much of the filepath is shared by start and path. - i = len(commonprefix([start_list, path_list])) - - rel_list = [pardir] * (len(start_list)-i) + path_list[i:] - if not rel_list: - return curdir - return join(*rel_list) - elif 'nt' in sys.builtin_module_names: - def relpath(path, start=os.path.curdir): - """Return a relative version of a path""" - from os.path import sep, curdir, join, abspath, commonprefix, \ - pardir, splitunc - - if not path: - raise ValueError("no path specified") - start_list = abspath(start).split(sep) - path_list = abspath(path).split(sep) - if start_list[0].lower() != path_list[0].lower(): - unc_path, rest = splitunc(path) - unc_start, rest = splitunc(start) - if bool(unc_path) ^ bool(unc_start): - raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" - % (path, start)) - else: - raise ValueError("path is on drive %s, start on drive %s" - % (path_list[0], start_list[0])) - # Work out how much of the filepath is shared by start and path. - for i in range(min(len(start_list), len(path_list))): - if start_list[i].lower() != path_list[i].lower(): - break - else: - i += 1 - - rel_list = [pardir] * (len(start_list)-i) + path_list[i:] - if not rel_list: - return curdir - return join(*rel_list) - else: - raise RuntimeError("Unsupported platform (no relpath available!)") - #------------------------------------------------------------------------------ # Registration hook #------------------------------------------------------------------------------ From d1388d35dcc382f6e3ea6843a4ecea6cccdf676f Mon Sep 17 00:00:00 2001 From: Matthew Brett Date: Mon, 14 Jul 2014 13:48:09 -0400 Subject: [PATCH 25/90] STY: typos in plot_directive docstrings Well, behaviour -> behavior is against my origins, but hey, we're all American now. --- lib/matplotlib/sphinxext/plot_directive.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/sphinxext/plot_directive.py b/lib/matplotlib/sphinxext/plot_directive.py index 4d2519f755c9..0015f84b3eb8 100644 --- a/lib/matplotlib/sphinxext/plot_directive.py +++ b/lib/matplotlib/sphinxext/plot_directive.py @@ -92,7 +92,7 @@ plot_basedir Base directory, to which ``plot::`` file names are relative to. (If None or empty, file names are relative to the - directoly where the file containing the directive is.) + directory where the file containing the directive is.) plot_formats File formats to generate. List of tuples or strings:: @@ -111,7 +111,7 @@ plot_apply_rcparams By default, rcParams are applied when `context` option is not used in - a plot directive. This configuration option overrides this behaviour + a plot directive. This configuration option overrides this behavior and applies rcParams before each plot. plot_working_directory @@ -123,9 +123,7 @@ helper modules for all code are located. plot_template - Provide a customized template for preparing resturctured text. - - + Provide a customized template for preparing restructured text. """ from __future__ import (absolute_import, division, print_function, unicode_literals) From 802e2675ccc72f48074cb81cce97ba22edd9a66a Mon Sep 17 00:00:00 2001 From: Matthew Brett Date: Mon, 14 Jul 2014 14:17:39 -0400 Subject: [PATCH 26/90] BUG: don't show parens, comma when no source link plot_directive displaying empty parens when no figures and no source required. Was also prepending a comma to list of images when there was source links not required. --- lib/matplotlib/sphinxext/plot_directive.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/sphinxext/plot_directive.py b/lib/matplotlib/sphinxext/plot_directive.py index 0015f84b3eb8..3e36998dc27c 100644 --- a/lib/matplotlib/sphinxext/plot_directive.py +++ b/lib/matplotlib/sphinxext/plot_directive.py @@ -346,9 +346,9 @@ def remove_coding(text): {{ only_html }} - {% if (source_link and html_show_source_link) or (html_show_formats and not multi_image) %} + {% if source_link or (html_show_formats and not multi_image) %} ( - {%- if source_link and html_show_source_link -%} + {%- if source_link -%} `Source code <{{ source_link }}>`__ {%- endif -%} {%- if html_show_formats and not multi_image -%} @@ -768,7 +768,9 @@ def run(arguments, content, options, state_machine, state, lineno): only_latex = ".. only:: latex" only_texinfo = ".. only:: texinfo" - if j == 0: + # Not-None src_link signals the need for a source link in the generated + # html + if j == 0 and config.plot_html_show_source_link: src_link = source_link else: src_link = None @@ -778,7 +780,6 @@ def run(arguments, content, options, state_machine, state, lineno): dest_dir=dest_dir_link, build_dir=build_dir_link, source_link=src_link, - html_show_source_link=config.plot_html_show_source_link, multi_image=len(images) > 1, only_html=only_html, only_latex=only_latex, @@ -786,7 +787,7 @@ def run(arguments, content, options, state_machine, state, lineno): options=opts, images=images, source_code=source_code, - html_show_formats=config.plot_html_show_formats, + html_show_formats=config.plot_html_show_formats and not nofigs, caption=caption) total_lines.extend(result.split("\n")) From 8755041777a6f790dc1ecbbc3b6c2d694e60012e Mon Sep 17 00:00:00 2001 From: Katy Huff Date: Mon, 14 Jul 2014 14:28:20 -0700 Subject: [PATCH 27/90] puts pythonxy back in --- INSTALL | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/INSTALL b/INSTALL index 1b7150f2cd98..f6b7d396343b 100644 --- a/INSTALL +++ b/INSTALL @@ -17,7 +17,9 @@ Python distribution `(Anaconda) distribution `(Canopy) `_ are both excellent choices that "just work" out of the box for any platform. Both of these packages include matplotlib and -*lots* of other useful tools. matplotlib is also packaged for almost +*lots* of other useful tools. +Another excellent alternative for Windows users is `Python (x, y) +`_ . matplotlib is also packaged for almost every major Linux distribution. So if you are on Linux, your package manager will probably provide matplotlib prebuilt. From 4efb43a99eceba35f85879d146b0973d0d919f5f Mon Sep 17 00:00:00 2001 From: Katy Huff Date: Wed, 16 Jul 2014 08:25:48 -0700 Subject: [PATCH 28/90] python(x,y) is now on google code? --- INSTALL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL b/INSTALL index f6b7d396343b..3a72974ca192 100644 --- a/INSTALL +++ b/INSTALL @@ -19,7 +19,7 @@ are both excellent choices that "just work" out of the box for any platform. Both of these packages include matplotlib and *lots* of other useful tools. Another excellent alternative for Windows users is `Python (x, y) -`_ . matplotlib is also packaged for almost +`_ . matplotlib is also packaged for almost every major Linux distribution. So if you are on Linux, your package manager will probably provide matplotlib prebuilt. From 16cc1c466c5194407338bd0c6f693669d0fcca2e Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Wed, 16 Jul 2014 12:38:21 -0400 Subject: [PATCH 29/90] Upload artifacts only on main repository. --- .travis.yml | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index e31cdf53008a..3c4283dcc980 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,23 +4,16 @@ env: - ARTIFACTS_S3_BUCKET=matplotlib-test-results - secure: RgJI7BBL8aX5FTOQe7xiXqWHMxWokd6GNUWp1NUV2mRLXPb9dI0RXqZt3UJwKTAzf1z/OtlHDmEkBoTVK81E9iUxK5npwyyjhJ8yTJmwfQtQF2n51Q1Ww9p+XSLORrOzZc7kAo6Kw6FIXN1pfctgYq2bQkrwJPRx/oPR8f6hcbY= - secure: E7OCdqhZ+PlwJcn+Hd6ns9TDJgEUXiUNEI0wu7xjxB2vBRRIKtZMbuaZjd+iKDqCKuVOJKu0ClBUYxmgmpLicTwi34CfTUYt6D4uhrU+8hBBOn1iiK51cl/aBvlUUrqaRLVhukNEBGZcyqAjXSA/Qsnp2iELEmAfOUa92ZYo1sk= - - matrix: - TEST_ARGS=--no-pep8 -before_script: - - gem install travis-artifacts - language: python -python: - - 2.6 - - 2.7 - - 3.3 - - 3.4 - matrix: include: + - python: 2.6 + - python: 2.7 + - python: 3.3 + - python: 3.4 - python: 2.7 env: TEST_ARGS=--pep8 @@ -43,6 +36,6 @@ script: after_failure: - tar cjf result_images.tar.bz2 result_images - - travis-artifacts upload --path result_images.tar.bz2 - - echo "The result images will only be uploaded if they are on the matplotlib/matplotlib repo - this is for security reasons to prevent arbitrary PRs echoing security details." - - echo https://s3.amazonaws.com/matplotlib-test-results/artifacts/${TRAVIS_BUILD_NUMBER}/${TRAVIS_JOB_NUMBER}/result_images.tar.bz2 + - if [[ $TRAVIS_PULL_REQUEST == false ]]; then gem install travis-artifacts; fi + - if [[ $TRAVIS_PULL_REQUEST == falst ]]; then travis-artifacts upload --path result_images.tar.bz2; fi + - if [[ $TRAVIS_PULL_REQUEST != false ]]; then echo "The result images will only be uploaded if they are on the matplotlib/matplotlib repo - this is for security reasons to prevent arbitrary PRs echoing security details." else echo https://s3.amazonaws.com/matplotlib-test-results/artifacts/${TRAVIS_BUILD_NUMBER}/${TRAVIS_JOB_NUMBER}/result_images.tar.bz2; fi From bd677835874aa4f74d115644d60526098d5104be Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Fri, 11 Jul 2014 17:52:56 -0500 Subject: [PATCH 30/90] Rewrite checks to use communicate. This ensures that file handles are cloesed --- lib/matplotlib/__init__.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index b787b5a9a057..d11b9d1c9817 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -343,8 +343,9 @@ def checkdep_dvipng(): try: s = subprocess.Popen(['dvipng','-version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - line = s.stdout.readlines()[1] - v = line.split()[-1].decode('ascii') + stdout, stderr = s.communicate() + line = stdout.decode('ascii').split('\n')[1] + v = line.split()[-1] return v except (IndexError, ValueError, OSError): return None @@ -371,7 +372,8 @@ def checkdep_tex(): try: s = subprocess.Popen(['tex','-version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - line = s.stdout.readlines()[0].decode('ascii') + stdout, stderr = s.communicate() + line = stdout.decode('ascii').split('\n')[0] pattern = '3\.1\d+' match = re.search(pattern, line) v = match.group(0) @@ -383,9 +385,10 @@ def checkdep_pdftops(): try: s = subprocess.Popen(['pdftops','-v'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - for line in s.stderr: - if b'version' in line: - v = line.split()[-1].decode('ascii') + stdout, stderr = s.communicate() + if b'version' in stderr: + line = stderr.decode('ascii').split('\n')[0] + v = line.split()[-1] return v except (IndexError, ValueError, UnboundLocalError, OSError): return None @@ -394,10 +397,9 @@ def checkdep_inkscape(): try: s = subprocess.Popen(['inkscape','-V'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - for line in s.stdout: - if b'Inkscape' in line: - v = line.split()[1].decode('ascii') - break + stdout, stderr = s.communicate() + if b'Inkscape' in stdout: + v = stdout.split()[1].decode('ascii') return v except (IndexError, ValueError, UnboundLocalError, OSError): return None @@ -406,10 +408,10 @@ def checkdep_xmllint(): try: s = subprocess.Popen(['xmllint','--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - for line in s.stderr: - if b'version' in line: - v = line.split()[-1].decode('ascii') - break + stdout, stderr = s.communicate() + if b'version' in stderr: + line = stderr.decode('ascii').split('\n')[0] + v = line.split()[-1] return v except (IndexError, ValueError, UnboundLocalError, OSError): return None From 04536ea788e965f77189948cafdd4273698c60c4 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 12 Jul 2014 09:38:18 -0500 Subject: [PATCH 31/90] Silence some more cloesed file warnings --- lib/matplotlib/backends/backend_ps.py | 9 +++++---- lib/matplotlib/tests/test_backend_ps.py | 4 +++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index 066cb00e1092..eaebf15bf4ae 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -90,12 +90,13 @@ def gs_version(self): pass from matplotlib.compat.subprocess import Popen, PIPE - pipe = Popen(self.gs_exe + " --version", - shell=True, stdout=PIPE).stdout + s = Popen(self.gs_exe + " --version", + shell=True, stdout=PIPE) + pipe, stderr = s.communicate() if six.PY3: - ver = pipe.read().decode('ascii') + ver = pipe.decode('ascii') else: - ver = pipe.read() + ver = pipe gs_version = tuple(map(int, ver.strip().split("."))) self._cached["gs_version"] = gs_version diff --git a/lib/matplotlib/tests/test_backend_ps.py b/lib/matplotlib/tests/test_backend_ps.py index 1b6aac6e37e5..fc82b6a53d55 100644 --- a/lib/matplotlib/tests/test_backend_ps.py +++ b/lib/matplotlib/tests/test_backend_ps.py @@ -40,6 +40,8 @@ def _test_savefig_to_stringio(format='ps'): assert values[0] == values[1] assert values[1] == values[2].replace(b'\r\n', b'\n') + for buffer in buffers: + buffer.close() @cleanup @@ -80,4 +82,4 @@ def test_savefig_to_stringio_with_usetex_eps(): if __name__ == '__main__': import nose - nose.runmodule(argv=['-s', '--with-doctest'], exit=False) + nose.runmodule(argv=['-s', '--with-doctest', '-verbose'], exit=False) From 78e7531f2ff662b505f73ec6caba95b129535a4c Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 12 Jul 2014 10:11:49 -0500 Subject: [PATCH 32/90] Ensure that file is closed in texmanager --- lib/matplotlib/texmanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/texmanager.py b/lib/matplotlib/texmanager.py index 51758fe6851e..240155b489c7 100644 --- a/lib/matplotlib/texmanager.py +++ b/lib/matplotlib/texmanager.py @@ -70,10 +70,10 @@ def dvipng_hack_alpha(): try: p = Popen(['dvipng', '-version'], stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=(sys.platform != 'win32')) + stdin, stderr = p.communicate() except OSError: mpl.verbose.report('No dvipng was found', 'helpful') return False - stdin, stdout = p.stdin, p.stdout for line in stdout: if line.startswith(b'dvipng '): version = line.split()[-1] From dfcbe975675050a84e7a5bfd0b015154e52a0e52 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 12 Jul 2014 10:18:55 -0500 Subject: [PATCH 33/90] Remove leftover debug --- lib/matplotlib/tests/test_backend_ps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_backend_ps.py b/lib/matplotlib/tests/test_backend_ps.py index fc82b6a53d55..93aca46e28c1 100644 --- a/lib/matplotlib/tests/test_backend_ps.py +++ b/lib/matplotlib/tests/test_backend_ps.py @@ -82,4 +82,4 @@ def test_savefig_to_stringio_with_usetex_eps(): if __name__ == '__main__': import nose - nose.runmodule(argv=['-s', '--with-doctest', '-verbose'], exit=False) + nose.runmodule(argv=['-s', '--with-doctest'], exit=False) From cd3b7750268902e878375e2721cff3ee8be5e9a0 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 12 Jul 2014 14:12:31 -0500 Subject: [PATCH 34/90] Correct stdout in dvipng_hack --- lib/matplotlib/texmanager.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/texmanager.py b/lib/matplotlib/texmanager.py index 240155b489c7..8ff931a0d410 100644 --- a/lib/matplotlib/texmanager.py +++ b/lib/matplotlib/texmanager.py @@ -70,16 +70,16 @@ def dvipng_hack_alpha(): try: p = Popen(['dvipng', '-version'], stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=(sys.platform != 'win32')) - stdin, stderr = p.communicate() + stdout, stderr = p.communicate() except OSError: mpl.verbose.report('No dvipng was found', 'helpful') return False - for line in stdout: - if line.startswith(b'dvipng '): + lines = stdout.decode('ascii').split('\n') + for line in lines: + if line.startswith('dvipng '): version = line.split()[-1] mpl.verbose.report('Found dvipng version %s' % version, 'helpful') - version = version.decode('ascii') version = distutils.version.LooseVersion(version) return version < distutils.version.LooseVersion('1.6') mpl.verbose.report('Unexpected response from dvipng -version', 'helpful') From 5728b56dae83919f9ce48ca48e9eff5825fe7297 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sun, 13 Jul 2014 09:13:55 -0500 Subject: [PATCH 35/90] Change checks to more closly match the original --- lib/matplotlib/__init__.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index d11b9d1c9817..220ae10f74c8 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -386,9 +386,10 @@ def checkdep_pdftops(): s = subprocess.Popen(['pdftops','-v'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = s.communicate() - if b'version' in stderr: - line = stderr.decode('ascii').split('\n')[0] - v = line.split()[-1] + lines = stderr.decode('ascii').split('\n') + for line in lines: + if 'version' in line: + v = line.split()[-1] return v except (IndexError, ValueError, UnboundLocalError, OSError): return None @@ -398,8 +399,10 @@ def checkdep_inkscape(): s = subprocess.Popen(['inkscape','-V'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = s.communicate() - if b'Inkscape' in stdout: - v = stdout.split()[1].decode('ascii') + lines = stdout.decode('ascii').split('\n') + for line in lines: + if 'Inkscape' in line: + v = line.split()[1] return v except (IndexError, ValueError, UnboundLocalError, OSError): return None @@ -409,9 +412,10 @@ def checkdep_xmllint(): s = subprocess.Popen(['xmllint','--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = s.communicate() - if b'version' in stderr: - line = stderr.decode('ascii').split('\n')[0] - v = line.split()[-1] + lines = stderr.decode('ascii').split('\n') + for line in lines: + if 'version' in line: + v = line.split()[-1] return v except (IndexError, ValueError, UnboundLocalError, OSError): return None From 461264eb5255eac57506069914a4c57cb0cbb67b Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sun, 13 Jul 2014 09:16:57 -0500 Subject: [PATCH 36/90] Add breaks to match original --- lib/matplotlib/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 220ae10f74c8..25d7e04f3103 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -403,6 +403,7 @@ def checkdep_inkscape(): for line in lines: if 'Inkscape' in line: v = line.split()[1] + break return v except (IndexError, ValueError, UnboundLocalError, OSError): return None @@ -416,6 +417,7 @@ def checkdep_xmllint(): for line in lines: if 'version' in line: v = line.split()[-1] + break return v except (IndexError, ValueError, UnboundLocalError, OSError): return None From 0fb79140165b47e2e78c10e9075329d44f4a4996 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 21 Jul 2014 23:25:46 -0400 Subject: [PATCH 37/90] BUG : fix _reshape_2D bug with [(n, 1), ..] input cbook._reshape_2D the input can be _at most_ 2D, however if it is passed a list of (n, 1) shaped ndarray this condition will be violated and it fails to work (which is less than great, but fine from a documentation stand point). However, boxplot _used_ to work with a list of such ndarrays (and users might expect to call a (n, 1) shaped array a 1D array). The added `ravel` makes sure that even if we get a list of any dimensional ndarrays in, the output will be a list of (n, ) shaped arrays. Closes #3220 --- lib/matplotlib/cbook.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index d0978fa46d92..192f1d3037d1 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2168,6 +2168,8 @@ def _reshape_2D(X): if not hasattr(X[0], '__len__'): X = [X] + else: + X = [np.ravel(x) for x in X] return X From 5f68f646440e04630f3142d962503fafb14363b7 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 12 Jul 2014 12:05:03 -0500 Subject: [PATCH 38/90] Change num lines to int in hatch. Fixes numpy issues warnings --- lib/matplotlib/hatch.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/hatch.py b/lib/matplotlib/hatch.py index e6df8bcfce6d..ad703bd0e72b 100644 --- a/lib/matplotlib/hatch.py +++ b/lib/matplotlib/hatch.py @@ -21,7 +21,7 @@ class HatchPatternBase: class HorizontalHatch(HatchPatternBase): def __init__(self, hatch, density): - self.num_lines = (hatch.count('-') + hatch.count('+')) * density + self.num_lines = int((hatch.count('-') + hatch.count('+')) * density) self.num_vertices = self.num_lines * 2 def set_vertices_and_codes(self, vertices, codes): @@ -38,7 +38,7 @@ def set_vertices_and_codes(self, vertices, codes): class VerticalHatch(HatchPatternBase): def __init__(self, hatch, density): - self.num_lines = (hatch.count('|') + hatch.count('+')) * density + self.num_lines = int((hatch.count('|') + hatch.count('+')) * density) self.num_vertices = self.num_lines * 2 def set_vertices_and_codes(self, vertices, codes): @@ -55,8 +55,8 @@ def set_vertices_and_codes(self, vertices, codes): class NorthEastHatch(HatchPatternBase): def __init__(self, hatch, density): - self.num_lines = (hatch.count('/') + hatch.count('x') + - hatch.count('X')) * density + self.num_lines = int((hatch.count('/') + hatch.count('x') + + hatch.count('X')) * density) if self.num_lines: self.num_vertices = (self.num_lines + 1) * 2 else: @@ -74,8 +74,8 @@ def set_vertices_and_codes(self, vertices, codes): class SouthEastHatch(HatchPatternBase): def __init__(self, hatch, density): - self.num_lines = (hatch.count('\\') + hatch.count('x') + - hatch.count('X')) * density + self.num_lines = int((hatch.count('\\') + hatch.count('x') + + hatch.count('X')) * density) self.num_vertices = (self.num_lines + 1) * 2 if self.num_lines: self.num_vertices = (self.num_lines + 1) * 2 @@ -100,8 +100,8 @@ def __init__(self, hatch, density): self.num_shapes = 0 self.num_vertices = 0 else: - self.num_shapes = ((self.num_rows / 2 + 1) * (self.num_rows + 1) + - (self.num_rows / 2) * (self.num_rows)) + self.num_shapes = ((self.num_rows // 2 + 1) * (self.num_rows + 1) + + (self.num_rows // 2) * (self.num_rows)) self.num_vertices = (self.num_shapes * len(self.shape_vertices) * (self.filled and 1 or 2)) @@ -212,6 +212,7 @@ def get_path(hatchpattern, density=6): cursor = 0 for pattern in patterns: if pattern.num_vertices != 0: + num_vertices vertices_chunk = vertices[cursor:cursor + pattern.num_vertices] codes_chunk = codes[cursor:cursor + pattern.num_vertices] pattern.set_vertices_and_codes(vertices_chunk, codes_chunk) From 687f1f8e2637a7f2a54d069902996151261dd246 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 12 Jul 2014 12:06:31 -0500 Subject: [PATCH 39/90] Index with an integer in test_axes --- lib/matplotlib/tests/test_axes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 638e4907095a..45d791341d30 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -523,7 +523,7 @@ def test_nonfinite_limits(): y = np.log(x) finally: np.seterr(**olderr) - x[len(x)/2] = np.nan + x[len(x)//2] = np.nan fig = plt.figure() ax = fig.add_subplot(111) ax.plot(x, y) From 4d3b23abe1928d22ac9c3531f6415d1c1c1f9247 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 12 Jul 2014 12:12:15 -0500 Subject: [PATCH 40/90] Number of verts in simplification is an int Fixes numpy warnings in indexing --- lib/matplotlib/tests/test_simplification.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_simplification.py b/lib/matplotlib/tests/test_simplification.py index a1bc217bf6fd..e99ac097a989 100644 --- a/lib/matplotlib/tests/test_simplification.py +++ b/lib/matplotlib/tests/test_simplification.py @@ -153,7 +153,7 @@ def test_start_with_moveto(): decodebytes = base64.decodestring verts = np.fromstring(decodebytes(data), dtype=' Date: Sat, 12 Jul 2014 13:19:21 -0500 Subject: [PATCH 41/90] Explicite cast to int in colors --- lib/matplotlib/colors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index dbc96197c134..84b8efca403f 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -222,7 +222,7 @@ def is_color_like(c): def rgb2hex(rgb): 'Given an rgb or rgba sequence of 0-1 floats, return the hex string' - return '#%02x%02x%02x' % tuple([np.round(val * 255) for val in rgb[:3]]) + return '#%02x%02x%02x' % tuple([int(np.round(val * 255)) for val in rgb[:3]]) hexColorPattern = re.compile("\A#[a-fA-F0-9]{6}\Z") From 82496ee81bdb3a32f7445bb2b6d946bb1e0b099f Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 12 Jul 2014 13:32:06 -0500 Subject: [PATCH 42/90] Cast steps to integer in cbook --- lib/matplotlib/cbook.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index d0978fa46d92..e0da386583d3 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -1737,7 +1737,7 @@ def simple_linear_interpolation(a, steps): if steps == 1: return a - steps = np.floor(steps) + steps = int(np.floor(steps)) new_length = ((len(a) - 1) * steps) + 1 new_shape = list(a.shape) new_shape[0] = new_length From d7a5c8e19209c85221abfd55cf3ba07b0fd81baf Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 12 Jul 2014 13:39:56 -0500 Subject: [PATCH 43/90] Cast to integer in bezier for indexing --- lib/matplotlib/bezier.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/matplotlib/bezier.py b/lib/matplotlib/bezier.py index 034aa1d45cd1..efa657795a15 100644 --- a/lib/matplotlib/bezier.py +++ b/lib/matplotlib/bezier.py @@ -256,7 +256,7 @@ def split_path_inout(path, inside, tolerence=0.01, reorder_inout=False): for ctl_points, command in path_iter: iold = i - i += len(ctl_points) / 2 + i += len(ctl_points) // 2 if inside(ctl_points[-2:]) != begin_inside: bezier_path = concat([ctl_points_old[-2:], ctl_points]) break @@ -286,7 +286,6 @@ def split_path_inout(path, inside, tolerence=0.01, reorder_inout=False): verts_right = right[:] #i += 1 - if path.codes is None: path_in = Path(concat([path.vertices[:i], verts_left])) path_out = Path(concat([verts_right, path.vertices[i:]])) From dc87d3b3eab3499ef9e103218dc425819f46fa48 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 12 Jul 2014 13:58:51 -0500 Subject: [PATCH 44/90] Cast to integer in contour as suggested by comment --- lib/matplotlib/bezier.py | 1 + lib/matplotlib/contour.py | 2 +- lib/matplotlib/hatch.py | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/bezier.py b/lib/matplotlib/bezier.py index efa657795a15..ce2c9d5052a1 100644 --- a/lib/matplotlib/bezier.py +++ b/lib/matplotlib/bezier.py @@ -286,6 +286,7 @@ def split_path_inout(path, inside, tolerence=0.01, reorder_inout=False): verts_right = right[:] #i += 1 + if path.codes is None: path_in = Path(concat([path.vertices[:i], verts_left])) path_out = Path(concat([verts_right, path.vertices[i:]])) diff --git a/lib/matplotlib/contour.py b/lib/matplotlib/contour.py index 383978cbc44d..9de179f355c0 100644 --- a/lib/matplotlib/contour.py +++ b/lib/matplotlib/contour.py @@ -475,7 +475,7 @@ def calc_label_rot_and_inline(self, slc, ind, lw, lc=None, spacing=5): pl, lc, [xi[1]]) # Make integer - I = [np.floor(I[0]), np.ceil(I[1])] + I = [int(np.floor(I[0])), int(np.ceil(I[1]))] # Actually break contours if closed: diff --git a/lib/matplotlib/hatch.py b/lib/matplotlib/hatch.py index ad703bd0e72b..6729b610997c 100644 --- a/lib/matplotlib/hatch.py +++ b/lib/matplotlib/hatch.py @@ -212,7 +212,6 @@ def get_path(hatchpattern, density=6): cursor = 0 for pattern in patterns: if pattern.num_vertices != 0: - num_vertices vertices_chunk = vertices[cursor:cursor + pattern.num_vertices] codes_chunk = codes[cursor:cursor + pattern.num_vertices] pattern.set_vertices_and_codes(vertices_chunk, codes_chunk) From 8993188032f29bf94c2bb1e2aaefaab918354153 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 12 Jul 2014 14:36:54 -0500 Subject: [PATCH 45/90] Integer division in mlab test --- lib/matplotlib/tests/test_mlab.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/tests/test_mlab.py b/lib/matplotlib/tests/test_mlab.py index 0cc991f05ebf..5dcb5d790d37 100644 --- a/lib/matplotlib/tests/test_mlab.py +++ b/lib/matplotlib/tests/test_mlab.py @@ -1325,8 +1325,8 @@ def createStim(self, fstims, iscomplex, sides, nsides, len_x=None, freqs_specgram = freqs_density # time points for specgram - t_start = NFFT_specgram_real/2 - t_stop = len(x) - NFFT_specgram_real/2+1 + t_start = NFFT_specgram_real//2 + t_stop = len(x) - NFFT_specgram_real//2+1 t_step = NFFT_specgram_real - nover_specgram_real t_specgram = x[t_start:t_stop:t_step] if NFFT_specgram_real % 2: From 2c37ba1079f8ed7a07d1a9b8d3627622243e472f Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 12 Jul 2014 14:40:55 -0500 Subject: [PATCH 46/90] Integer division in trirefine --- lib/matplotlib/colors.py | 3 ++- lib/matplotlib/tri/trirefine.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index 84b8efca403f..5d165b8f69a0 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -222,7 +222,8 @@ def is_color_like(c): def rgb2hex(rgb): 'Given an rgb or rgba sequence of 0-1 floats, return the hex string' - return '#%02x%02x%02x' % tuple([int(np.round(val * 255)) for val in rgb[:3]]) + a = '#%02x%02x%02x' % tuple([int(np.round(val * 255)) for val in rgb[:3]]) + return a hexColorPattern = re.compile("\A#[a-fA-F0-9]{6}\Z") diff --git a/lib/matplotlib/tri/trirefine.py b/lib/matplotlib/tri/trirefine.py index ae2f257a1f5e..11e3fff3e468 100644 --- a/lib/matplotlib/tri/trirefine.py +++ b/lib/matplotlib/tri/trirefine.py @@ -231,7 +231,7 @@ def _refine_triangulation_once(triangulation, ancestors=None): # points # hint: each apex is shared by 2 masked_triangles except the borders. borders = np.sum(neighbors == -1) - added_pts = (3*ntri + borders) / 2 + added_pts = (3*ntri + borders) // 2 refi_npts = npts + added_pts refi_x = np.zeros(refi_npts) refi_y = np.zeros(refi_npts) From cd75c01e0af4a57bef34b7bc7cdaea2f41e4d302 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 12 Jul 2014 16:39:20 -0500 Subject: [PATCH 47/90] Filter warnings in rcparams test. --- lib/matplotlib/tests/test_rcparams.py | 45 ++++++++++++++------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/lib/matplotlib/tests/test_rcparams.py b/lib/matplotlib/tests/test_rcparams.py index 9d6d0613db76..e1859c6f5fe5 100644 --- a/lib/matplotlib/tests/test_rcparams.py +++ b/lib/matplotlib/tests/test_rcparams.py @@ -5,6 +5,7 @@ import os import sys +import warnings import matplotlib as mpl from matplotlib.tests import assert_str_equal @@ -97,29 +98,31 @@ def test_RcParams_class(): def test_Bug_2543(): # Test that it possible to add all values to itself / deepcopy - # This was not possible because validate_bool_maybe_none did not + # This was not possible because validate_bool_maybe_none did not # accept None as an argument. # https://github.com/matplotlib/matplotlib/issues/2543 - with mpl.rc_context(): - _copy = mpl.rcParams.copy() - for key in six.iterkeys(_copy): - mpl.rcParams[key] = _copy[key] - mpl.rcParams['text.dvipnghack'] = None - with mpl.rc_context(): - from copy import deepcopy - _deep_copy = deepcopy(mpl.rcParams) - from matplotlib.rcsetup import validate_bool_maybe_none, validate_bool - # real test is that this does not raise - assert_true(validate_bool_maybe_none(None) is None) - assert_true(validate_bool_maybe_none("none") is None) - _fonttype = mpl.rcParams['svg.fonttype'] - assert_true(_fonttype == mpl.rcParams['svg.embed_char_paths']) - with mpl.rc_context(): - mpl.rcParams['svg.embed_char_paths'] = False - assert_true(mpl.rcParams['svg.fonttype'] == "none") + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', message='.*(deprecated|obsolete)', category=UserWarning) + with mpl.rc_context(): + _copy = mpl.rcParams.copy() + for key in six.iterkeys(_copy): + mpl.rcParams[key] = _copy[key] + mpl.rcParams['text.dvipnghack'] = None + with mpl.rc_context(): + from copy import deepcopy + _deep_copy = deepcopy(mpl.rcParams) + from matplotlib.rcsetup import validate_bool_maybe_none, validate_bool + # real test is that this does not raise + assert_true(validate_bool_maybe_none(None) is None) + assert_true(validate_bool_maybe_none("none") is None) + _fonttype = mpl.rcParams['svg.fonttype'] + assert_true(_fonttype == mpl.rcParams['svg.embed_char_paths']) + with mpl.rc_context(): + mpl.rcParams['svg.embed_char_paths'] = False + assert_true(mpl.rcParams['svg.fonttype'] == "none") def test_Bug_2543_newer_python(): - # only split from above because of the usage of assert_raises + # only split from above because of the usage of assert_raises # as a context manager, which only works in 2.7 and above if sys.version_info[:2] < (2, 7): raise nose.SkipTest("assert_raises as context manager not supported with Python < 2.7") @@ -130,8 +133,8 @@ def test_Bug_2543_newer_python(): validate_bool(None) with assert_raises(ValueError): with mpl.rc_context(): - mpl.rcParams['svg.fonttype'] = True - + mpl.rcParams['svg.fonttype'] = True + if __name__ == '__main__': import nose nose.runmodule(argv=['-s', '--with-doctest'], exit=False) From c56ece1dc7c432004b4fd6c7090dd3949b33ecd2 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 12 Jul 2014 17:34:44 -0500 Subject: [PATCH 48/90] Suppress some more warnings. These are genuine too so we filter them --- lib/matplotlib/tests/test_subplots.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/tests/test_subplots.py b/lib/matplotlib/tests/test_subplots.py index b54158bb6de5..50c6563bf3c2 100644 --- a/lib/matplotlib/tests/test_subplots.py +++ b/lib/matplotlib/tests/test_subplots.py @@ -1,6 +1,7 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) +import warnings import six from six.moves import xrange @@ -105,10 +106,14 @@ def test_exceptions(): # TODO should this test more options? assert_raises(ValueError, plt.subplots, 2, 2, sharex='blah') assert_raises(ValueError, plt.subplots, 2, 2, sharey='blah') - assert_raises(ValueError, plt.subplots, 2, 2, -1) - # uncomment this for 1.5 - # assert_raises(ValueError, plt.subplots, 2, 2, 0) - assert_raises(ValueError, plt.subplots, 2, 2, 5) + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', + message='.*sharex\ argument\ to\ subplots', + category=UserWarning) + assert_raises(ValueError, plt.subplots, 2, 2, -1) + # uncomment this for 1.5 + # assert_raises(ValueError, plt.subplots, 2, 2, 0) + assert_raises(ValueError, plt.subplots, 2, 2, 5) @image_comparison(baseline_images=['subplots_offset_text'], remove_text=False) From ef2c5fab05565bbe82693745025fcfd1a3a2db8f Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 21 Jul 2014 15:20:23 +0100 Subject: [PATCH 49/90] Catch warning thrown in Mollweide projection. This happens when plotting tries to expand the interval for ticks beyond the defined region and do np.arcsin(y/sqrt(2)) where y is larger that sqrt(2) since the original limites matches the allowed interval. And Improve warnings message to clarify that this is related to ticks only. Also some PEP8 around the code that I changed. --- lib/matplotlib/axis.py | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 80ceed5de7e3..796a599cf417 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1010,20 +1010,32 @@ def _update_ticks(self, renderer): # the idea that one would rather potentially lose a tick # from one side of the axis or another, rather than see a # stack trace. - try: - ds1 = self._get_pixel_distance_along_axis(interval_expanded[0], -0.5) - except: - warnings.warn("Unable to find pixel distance along axis for interval padding; assuming no interval padding needed.") - ds1 = 0.0 - if np.isnan(ds1): - ds1 = 0.0 - try: - ds2 = self._get_pixel_distance_along_axis(interval_expanded[1], +0.5) - except: - warnings.warn("Unable to find pixel distance along axis for interval padding; assuming no interval padding needed.") - ds2 = 0.0 - if np.isnan(ds2): - ds2 = 0.0 + # We also catch users warnings here. These are the result of + # invalid numpy calculations that may be the result of out of + # bounds on axis with finite allowed intervals such as geo + # projections i.e. Mollweide. + with warnings.catch_warnings(): + warnings.simplefilter('ignore', category=RuntimeWarning) + try: + ds1 = self._get_pixel_distance_along_axis( + interval_expanded[0], -0.5) + except: + warnings.warn("Unable to find pixel distance along axis for\ + interval padding of ticks; assuming no interval padding\ + needed.") + ds1 = 0.0 + if np.isnan(ds1): + ds1 = 0.0 + try: + ds2 = self._get_pixel_distance_along_axis( + interval_expanded[1], +0.5) + except: + warnings.warn("Unable to find pixel distance along axis for\ + interval padding of ticks; assuming no interval padding\ + needed.") + ds2 = 0.0 + if np.isnan(ds2): + ds2 = 0.0 interval_expanded = (interval_expanded[0] - ds1, interval_expanded[1] + ds2) From ffe211e342408ebef0d93c131b71fc7b3b9f881f Mon Sep 17 00:00:00 2001 From: Leo Singer Date: Mon, 21 Jul 2014 11:39:07 -0700 Subject: [PATCH 50/90] Filter warnings with more selective np.errstate context --- lib/matplotlib/axis.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 796a599cf417..6f2b24c1793f 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1014,8 +1014,7 @@ def _update_ticks(self, renderer): # invalid numpy calculations that may be the result of out of # bounds on axis with finite allowed intervals such as geo # projections i.e. Mollweide. - with warnings.catch_warnings(): - warnings.simplefilter('ignore', category=RuntimeWarning) + with np.errstate(invalid='ignore'): try: ds1 = self._get_pixel_distance_along_axis( interval_expanded[0], -0.5) From a94e43faaa61901d201ee798c5f8827ce18eecd6 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Wed, 23 Jul 2014 09:37:36 +0200 Subject: [PATCH 51/90] Remove redundant cast to int --- lib/matplotlib/cbook.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index e0da386583d3..f538e238542a 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -1747,7 +1747,6 @@ def simple_linear_interpolation(a, steps): a0 = a[0:-1] a1 = a[1:] delta = ((a1 - a0) / steps) - steps = int(steps) for i in range(1, steps): result[i::steps] = delta * i + a0 result[steps::steps] = a1 From a7e673ddcdf46374566e1e1689a57cf3512b7de7 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Wed, 23 Jul 2014 09:46:27 +0200 Subject: [PATCH 52/90] Delay the cast to int in contour.py This is to allow checking againt nan below Thangs @argriffing --- lib/matplotlib/contour.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/contour.py b/lib/matplotlib/contour.py index 9de179f355c0..0d74a3a8461d 100644 --- a/lib/matplotlib/contour.py +++ b/lib/matplotlib/contour.py @@ -474,20 +474,21 @@ def calc_label_rot_and_inline(self, slc, ind, lw, lc=None, spacing=5): xy2 = mlab.less_simple_linear_interpolation( pl, lc, [xi[1]]) - # Make integer - I = [int(np.floor(I[0])), int(np.ceil(I[1]))] + # Round to integer values but keep as float + # To allow check against nan below + I = [np.floor(I[0]), np.ceil(I[1])] # Actually break contours if closed: # This will remove contour if shorter than label if np.all(~np.isnan(I)): - nlc.append(np.r_[xy2, lc[I[1]:I[0] + 1], xy1]) + nlc.append(np.r_[xy2, lc[int(I[1]):int(I[0]) + 1], xy1]) else: # These will remove pieces of contour if they have length zero if not np.isnan(I[0]): - nlc.append(np.r_[lc[:I[0] + 1], xy1]) + nlc.append(np.r_[lc[:int(I[0]) + 1], xy1]) if not np.isnan(I[1]): - nlc.append(np.r_[xy2, lc[I[1]:]]) + nlc.append(np.r_[xy2, lc[int(I[1]):]]) # The current implementation removes contours completely # covered by labels. Uncomment line below to keep From 1aa2e584ec338d518ab8454c9836b4e6c11f2769 Mon Sep 17 00:00:00 2001 From: Levi Kilcher Date: Thu, 3 Jul 2014 15:05:34 -0600 Subject: [PATCH 53/90] Added 'double arrow' (DArrow) to patches.BoxStyle. --- doc/users/annotations_guide.rst | 1 + lib/matplotlib/patches.py | 51 +++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/doc/users/annotations_guide.rst b/doc/users/annotations_guide.rst index fa585eb9e30e..aae3e190d45d 100644 --- a/doc/users/annotations_guide.rst +++ b/doc/users/annotations_guide.rst @@ -48,6 +48,7 @@ keyword arguments. Currently, following box styles are implemented. ========== ============== ========================== LArrow ``larrow`` pad=0.3 RArrow ``rarrow`` pad=0.3 + DArrow ``darrow`` pad=0.3 Round ``round`` pad=0.3,rounding_size=None Round4 ``round4`` pad=0.3,rounding_size=None Roundtooth ``roundtooth`` pad=0.3,tooth_size=None diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py index aea11327dfdb..339f111c2a2f 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -2003,6 +2003,57 @@ def transmute(self, x0, y0, width, height, mutation_size): _style_list["rarrow"] = RArrow + class DArrow(_Base): + """ + (Double) Arrow Box + """ + # This source is copied from LArrow, + # modified to add a right arrow to the bbox. + + def __init__(self, pad=0.3): + self.pad = pad + super(BoxStyle.DArrow, self).__init__() + + def transmute(self, x0, y0, width, height, mutation_size): + + # padding + pad = mutation_size * self.pad + + # width and height with padding added. + # The width is padded by the arrows, so we don't need to pad it. + height = height + 2. * pad + + # boundary of the padded box + x0, y0 = x0 - pad, y0 - pad + x1, y1 = x0 + width, y0 + height + + dx = (y1 - y0)/2. + dxx = dx * .5 + # adjust x0. 1.4 <- sqrt(2) + x0 = x0 + pad / 1.4 + + cp = [(x0 + dxx, y0), (x1, y0), # bot-segment + (x1, y0 - dxx), (x1 + dx + dxx, y0 + dx), + (x1, y1 + dxx), # right-arrow + (x1, y1), (x0 + dxx, y1), # top-segment + (x0 + dxx, y1 + dxx), (x0 - dx, y0 + dx), + (x0 + dxx, y0 - dxx), # left-arrow + (x0 + dxx, y0), (x0 + dxx, y0)] # close-poly + + com = [Path.MOVETO, Path.LINETO, + Path.LINETO, Path.LINETO, + Path.LINETO, + Path.LINETO, Path.LINETO, + Path.LINETO, Path.LINETO, + Path.LINETO, + Path.LINETO, Path.CLOSEPOLY] + + path = Path(cp, com) + + return path + + _style_list['darrow'] = DArrow + class Round(_Base): """ A box with round corners. From 50ba22f220a5a518e90119df39e3105a03943c3a Mon Sep 17 00:00:00 2001 From: Levi Kilcher Date: Mon, 21 Jul 2014 18:55:31 -0600 Subject: [PATCH 54/90] Added image test for arrow patches. All bbox's in patches.BoxStyle are included in the test image. --- .../boxarrow_test_image.png | Bin 0 -> 42516 bytes lib/matplotlib/tests/test_arrow_patches.py | 21 ++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 lib/matplotlib/tests/baseline_images/test_arrow_patches/boxarrow_test_image.png diff --git a/lib/matplotlib/tests/baseline_images/test_arrow_patches/boxarrow_test_image.png b/lib/matplotlib/tests/baseline_images/test_arrow_patches/boxarrow_test_image.png new file mode 100644 index 0000000000000000000000000000000000000000..3ddad736d44af412c2bf3fb79a1802977f989d1e GIT binary patch literal 42516 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 Date: Wed, 23 Jul 2014 16:10:36 -0600 Subject: [PATCH 55/90] Update patches doc and example. Added Circle to annotations_guide.rst. Moved DArrow up so that entries are sorted. Made fancybox_demo2.py outputs sorted box demo (so that ordering matches annotations_guide). Increased spacing between boxes in fancybox_demo2 to prevent overlap of images. --- doc/users/annotations_guide.rst | 3 ++- examples/pylab_examples/fancybox_demo2.py | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/users/annotations_guide.rst b/doc/users/annotations_guide.rst index aae3e190d45d..70ef8c42fdaf 100644 --- a/doc/users/annotations_guide.rst +++ b/doc/users/annotations_guide.rst @@ -46,9 +46,10 @@ keyword arguments. Currently, following box styles are implemented. ========== ============== ========================== Class Name Attrs ========== ============== ========================== + Circle ``circle`` pad=0.3 + DArrow ``darrow`` pad=0.3 LArrow ``larrow`` pad=0.3 RArrow ``rarrow`` pad=0.3 - DArrow ``darrow`` pad=0.3 Round ``round`` pad=0.3,rounding_size=None Round4 ``round4`` pad=0.3,rounding_size=None Roundtooth ``roundtooth`` pad=0.3,tooth_size=None diff --git a/examples/pylab_examples/fancybox_demo2.py b/examples/pylab_examples/fancybox_demo2.py index 43a04a876b69..ae6cb99b680e 100644 --- a/examples/pylab_examples/fancybox_demo2.py +++ b/examples/pylab_examples/fancybox_demo2.py @@ -2,13 +2,14 @@ import matplotlib.pyplot as plt styles = mpatch.BoxStyle.get_styles() +spacing = 1.2 -figheight = (len(styles)+.5) +figheight = (spacing * len(styles) + .5) fig1 = plt.figure(1, (4/1.5, figheight/1.5)) fontsize = 0.3 * 72 -for i, (stylename, styleclass) in enumerate(styles.items()): - fig1.text(0.5, (float(len(styles)) - 0.5 - i)/figheight, stylename, +for i, stylename in enumerate(sorted(styles.keys())): + fig1.text(0.5, (spacing * (float(len(styles)) - i) - 0.5)/figheight, stylename, ha="center", size=fontsize, transform=fig1.transFigure, From 155e1f7a6c61ad3f94ad5e99150ad414e674331a Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 21 Jul 2014 16:27:47 +0100 Subject: [PATCH 56/90] Catch warnings in tightlayout test. These are warnings that are raised because tightlayout does not work fully automatic on gridspec. However, the point of the test is doing this with some manual help so this works as expected. --- lib/matplotlib/tests/test_tightlayout.py | 64 +++++++++++++----------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/lib/matplotlib/tests/test_tightlayout.py b/lib/matplotlib/tests/test_tightlayout.py index 14e1528faeba..19782233c303 100644 --- a/lib/matplotlib/tests/test_tightlayout.py +++ b/lib/matplotlib/tests/test_tightlayout.py @@ -2,6 +2,7 @@ unicode_literals) import six +import warnings import numpy as np @@ -10,12 +11,14 @@ from nose.tools import assert_raises from numpy.testing import assert_array_equal + def example_plot(ax, fontsize=12): - ax.plot([1, 2]) - ax.locator_params(nbins=3) - ax.set_xlabel('x-label', fontsize=fontsize) - ax.set_ylabel('y-label', fontsize=fontsize) - ax.set_title('Title', fontsize=fontsize) + ax.plot([1, 2]) + ax.locator_params(nbins=3) + ax.set_xlabel('x-label', fontsize=fontsize) + ax.set_ylabel('y-label', fontsize=fontsize) + ax.set_title('Title', fontsize=fontsize) + @image_comparison(baseline_images=['tight_layout1']) def test_tight_layout1(): @@ -81,50 +84,51 @@ def test_tight_layout5(): fig = plt.figure() ax = plt.subplot(111) - arr = np.arange(100).reshape((10,10)) + arr = np.arange(100).reshape((10, 10)) ax.imshow(arr, interpolation="none") plt.tight_layout() - @image_comparison(baseline_images=['tight_layout6']) def test_tight_layout6(): 'Test tight_layout for gridspec' - fig = plt.figure() + with warnings.catch_warnings(): + warnings.simplefilter("ignore", UserWarning) + fig = plt.figure() - import matplotlib.gridspec as gridspec + import matplotlib.gridspec as gridspec - gs1 = gridspec.GridSpec(2, 1) - ax1 = fig.add_subplot(gs1[0]) - ax2 = fig.add_subplot(gs1[1]) + gs1 = gridspec.GridSpec(2, 1) + ax1 = fig.add_subplot(gs1[0]) + ax2 = fig.add_subplot(gs1[1]) - example_plot(ax1) - example_plot(ax2) + example_plot(ax1) + example_plot(ax2) - gs1.tight_layout(fig, rect=[0, 0, 0.5, 1]) + gs1.tight_layout(fig, rect=[0, 0, 0.5, 1]) - gs2 = gridspec.GridSpec(3, 1) + gs2 = gridspec.GridSpec(3, 1) - for ss in gs2: - ax = fig.add_subplot(ss) - example_plot(ax) - ax.set_title("") - ax.set_xlabel("") + for ss in gs2: + ax = fig.add_subplot(ss) + example_plot(ax) + ax.set_title("") + ax.set_xlabel("") - ax.set_xlabel("x-label", fontsize=12) + ax.set_xlabel("x-label", fontsize=12) - gs2.tight_layout(fig, rect=[0.5, 0, 1, 1], h_pad=0.45) + gs2.tight_layout(fig, rect=[0.5, 0, 1, 1], h_pad=0.45) - top = min(gs1.top, gs2.top) - bottom = max(gs1.bottom, gs2.bottom) + top = min(gs1.top, gs2.top) + bottom = max(gs1.bottom, gs2.bottom) - gs1.tight_layout(fig, rect=[None, 0 + (bottom-gs1.bottom), - 0.5, 1 - (gs1.top-top)]) - gs2.tight_layout(fig, rect=[0.5, 0 + (bottom-gs2.bottom), - None, 1 - (gs2.top-top)], - h_pad=0.45) + gs1.tight_layout(fig, rect=[None, 0 + (bottom-gs1.bottom), + 0.5, 1 - (gs1.top-top)]) + gs2.tight_layout(fig, rect=[0.5, 0 + (bottom-gs2.bottom), + None, 1 - (gs2.top-top)], + h_pad=0.45) @image_comparison(baseline_images=['tight_layout7']) From 669c379dcc30e50987350c75063f8b65d0111c77 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 26 Jul 2014 11:13:38 +0200 Subject: [PATCH 57/90] Add note about disabling warnings in test_rcparams --- lib/matplotlib/tests/test_rcparams.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_rcparams.py b/lib/matplotlib/tests/test_rcparams.py index e1859c6f5fe5..928393ecd5c7 100644 --- a/lib/matplotlib/tests/test_rcparams.py +++ b/lib/matplotlib/tests/test_rcparams.py @@ -101,8 +101,13 @@ def test_Bug_2543(): # This was not possible because validate_bool_maybe_none did not # accept None as an argument. # https://github.com/matplotlib/matplotlib/issues/2543 + # We filter warnings at this stage since a number of them are raised + # for deprecated rcparams as they should. We dont want these in the + # printed in the test suite. with warnings.catch_warnings(): - warnings.filterwarnings('ignore', message='.*(deprecated|obsolete)', category=UserWarning) + warnings.filterwarnings('ignore', + message='.*(deprecated|obsolete)', + category=UserWarning) with mpl.rc_context(): _copy = mpl.rcParams.copy() for key in six.iterkeys(_copy): From 855407bba6a1cfcadc89d51f524236439216bdd9 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 26 Jul 2014 11:16:02 +0200 Subject: [PATCH 58/90] Add note to test_subplots --- lib/matplotlib/tests/test_subplots.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/tests/test_subplots.py b/lib/matplotlib/tests/test_subplots.py index 50c6563bf3c2..4400cbc4beb9 100644 --- a/lib/matplotlib/tests/test_subplots.py +++ b/lib/matplotlib/tests/test_subplots.py @@ -106,10 +106,12 @@ def test_exceptions(): # TODO should this test more options? assert_raises(ValueError, plt.subplots, 2, 2, sharex='blah') assert_raises(ValueError, plt.subplots, 2, 2, sharey='blah') + # We filter warnings in this test which are genuine since + # the pount of this test is to ensure that this raises. with warnings.catch_warnings(): warnings.filterwarnings('ignore', - message='.*sharex\ argument\ to\ subplots', - category=UserWarning) + message='.*sharex\ argument\ to\ subplots', + category=UserWarning) assert_raises(ValueError, plt.subplots, 2, 2, -1) # uncomment this for 1.5 # assert_raises(ValueError, plt.subplots, 2, 2, 0) From 8727205ced5b0037b8e7e45d1addba19f6e5efc6 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 26 Jul 2014 11:17:56 +0200 Subject: [PATCH 59/90] Add note to test_tightlayout --- lib/matplotlib/tests/test_tightlayout.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/matplotlib/tests/test_tightlayout.py b/lib/matplotlib/tests/test_tightlayout.py index 19782233c303..e6935cae8c6b 100644 --- a/lib/matplotlib/tests/test_tightlayout.py +++ b/lib/matplotlib/tests/test_tightlayout.py @@ -94,6 +94,9 @@ def test_tight_layout5(): def test_tight_layout6(): 'Test tight_layout for gridspec' + # This raises warnings since tight layout cannot + # do this fully automatically. But the test is + # correct since the layout is manually edited with warnings.catch_warnings(): warnings.simplefilter("ignore", UserWarning) fig = plt.figure() From 57040ac2be7d2c0196050929819ec2bc27e27311 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 26 Jul 2014 11:44:07 +0200 Subject: [PATCH 60/90] Suppress deprecation warning for Delaunay module in the test suite --- lib/matplotlib/tests/test_delaunay.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_delaunay.py b/lib/matplotlib/tests/test_delaunay.py index 49f10f811c93..373f77404fe0 100644 --- a/lib/matplotlib/tests/test_delaunay.py +++ b/lib/matplotlib/tests/test_delaunay.py @@ -3,10 +3,16 @@ import six from six.moves import xrange +import warnings import numpy as np from matplotlib.testing.decorators import image_comparison, knownfailureif -from matplotlib.delaunay.triangulate import Triangulation +from matplotlib.cbook import MatplotlibDeprecationWarning + +with warnings.catch_warnings(): + # the module is deprecated. The tests should be removed when the module is. + warnings.simplefilter('ignore', MatplotlibDeprecationWarning) + from matplotlib.delaunay.triangulate import Triangulation from matplotlib import pyplot as plt import matplotlib as mpl From 1b7856e27006c4c09157f6741f6ebe1eaa3e09e7 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 26 Jul 2014 12:49:28 +0200 Subject: [PATCH 61/90] Suppress warning due to NaNs in this test. --- lib/matplotlib/tests/test_mlab.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_mlab.py b/lib/matplotlib/tests/test_mlab.py index 0cc991f05ebf..d811c8eefe3d 100644 --- a/lib/matplotlib/tests/test_mlab.py +++ b/lib/matplotlib/tests/test_mlab.py @@ -2714,7 +2714,10 @@ def get_z(x, y): z_masked = np.ma.array(z, mask=[False, False, False, True, False]) correct_zi_masked = np.ma.masked_where(xi + yi > 1.0, get_z(xi, yi)) zi = mlab.griddata(x, y, z_masked, xi, yi, interp='linear') - np.testing.assert_array_almost_equal(zi, correct_zi_masked) + with np.errstate(invalid='ignore'): + # The array contains a number of NaNs so suppress the + # numpy warnings here. + np.testing.assert_array_almost_equal(zi, correct_zi_masked) np.testing.assert_array_equal(np.ma.getmask(zi), np.ma.getmask(correct_zi_masked)) From 58ac6a841e323e6dd0be4152c9492054ea05c098 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 26 Jul 2014 18:21:27 +0200 Subject: [PATCH 62/90] Suppress warning when handling NaNs in streamplot --- lib/matplotlib/tests/test_streamplot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_streamplot.py b/lib/matplotlib/tests/test_streamplot.py index d040fccb12a5..75b9e082da25 100644 --- a/lib/matplotlib/tests/test_streamplot.py +++ b/lib/matplotlib/tests/test_streamplot.py @@ -42,7 +42,8 @@ def test_masks_and_nans(): mask[40:60, 40:60] = 1 U = np.ma.array(U, mask=mask) U[:20, :20] = np.nan - plt.streamplot(X, Y, U, V, color=U, cmap=plt.cm.Blues) + with np.errstate(invalid='ignore'): + plt.streamplot(X, Y, U, V, color=U, cmap=plt.cm.Blues) @cleanup From edffd3caa0f0a451ee1e12ae10f410754df5a3fb Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 26 Jul 2014 18:58:15 +0200 Subject: [PATCH 63/90] Use assert_array_almost_equal from numpy.ma This does not raise warnings about NaNs in the masked part of the array. --- lib/matplotlib/tests/test_triangulation.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/tests/test_triangulation.py b/lib/matplotlib/tests/test_triangulation.py index 90a8fe9d23d1..87719c29a63d 100644 --- a/lib/matplotlib/tests/test_triangulation.py +++ b/lib/matplotlib/tests/test_triangulation.py @@ -9,6 +9,7 @@ from nose.tools import assert_equal, assert_raises from numpy.testing import assert_array_equal, assert_array_almost_equal,\ assert_array_less +import numpy.ma.testutils as matest from matplotlib.testing.decorators import image_comparison import matplotlib.cm as cm from matplotlib.path import Path @@ -323,7 +324,7 @@ def test_triinterp(): xs, ys = np.meshgrid(xs, ys) for interp in (linear_interp, cubic_min_E, cubic_geom): zs = interp(xs, ys) - assert_array_almost_equal(zs, (1.23*xs - 4.79*ys)) + matest.assert_array_almost_equal(zs, (1.23*xs - 4.79*ys)) mask = (xs >= 1) * (xs <= 2) * (ys >= 1) * (ys <= 2) assert_array_equal(zs.mask, mask) @@ -697,7 +698,8 @@ def z(x, y): interp_z0[interp_key] = interp(xs0, ys0) # storage else: interpz = interp(xs, ys) - assert_array_almost_equal(interpz, interp_z0[interp_key]) + matest.assert_array_almost_equal(interpz, + interp_z0[interp_key]) scale_factor = 987654.3210 for scaled_axis in ('x', 'y'): @@ -723,7 +725,7 @@ def z(x, y): # 1 axis... for interp_key in ['lin', 'min_E', 'geom']: interpz = dic_interp[interp_key](xs, ys) - assert_array_almost_equal(interpz, interp_z0[interp_key]) + matest.assert_array_almost_equal(interpz, interp_z0[interp_key]) @image_comparison(baseline_images=['tri_smooth_contouring'], From 827393e0a6f7dd6c9516747b2cd8e67a48a19234 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 26 Jul 2014 19:01:47 +0200 Subject: [PATCH 64/90] Use ma.testutils rather than a filter for warnings with NaN in masked arrays in test_mlab --- lib/matplotlib/tests/test_mlab.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/tests/test_mlab.py b/lib/matplotlib/tests/test_mlab.py index d811c8eefe3d..36935793e05a 100644 --- a/lib/matplotlib/tests/test_mlab.py +++ b/lib/matplotlib/tests/test_mlab.py @@ -6,6 +6,7 @@ import tempfile from numpy.testing import assert_allclose, assert_array_equal +import numpy.ma.testutils as matest import numpy as np from nose.tools import (assert_equal, assert_almost_equal, assert_not_equal, assert_true, assert_raises) @@ -2714,10 +2715,7 @@ def get_z(x, y): z_masked = np.ma.array(z, mask=[False, False, False, True, False]) correct_zi_masked = np.ma.masked_where(xi + yi > 1.0, get_z(xi, yi)) zi = mlab.griddata(x, y, z_masked, xi, yi, interp='linear') - with np.errstate(invalid='ignore'): - # The array contains a number of NaNs so suppress the - # numpy warnings here. - np.testing.assert_array_almost_equal(zi, correct_zi_masked) + matest.assert_array_almost_equal(zi, correct_zi_masked) np.testing.assert_array_equal(np.ma.getmask(zi), np.ma.getmask(correct_zi_masked)) From 9aea0d4e4a66f9a2214f6cf722649471125c4d44 Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Tue, 29 Jul 2014 16:56:43 +0200 Subject: [PATCH 65/90] Fixed error with QSizePolicy Changed QtGui.QSizePolicy to QtWidgets.SizePolicy as the namepace has changed from Qt4 to Qt5 --- lib/matplotlib/backends/backend_qt5.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/backends/backend_qt5.py b/lib/matplotlib/backends/backend_qt5.py index b54d23e80daf..bbbb007edd0d 100644 --- a/lib/matplotlib/backends/backend_qt5.py +++ b/lib/matplotlib/backends/backend_qt5.py @@ -610,8 +610,8 @@ def _init_toolbar(self): self.locLabel.setAlignment( QtCore.Qt.AlignRight | QtCore.Qt.AlignTop) self.locLabel.setSizePolicy( - QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, - QtGui.QSizePolicy.Ignored)) + QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, + QtWidgets.QSizePolicy.Ignored)) labelAction = self.addWidget(self.locLabel) labelAction.setVisible(True) From c01d5e6fc85bfbf45662b5b707e8709c2a03883e Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Tue, 29 Jul 2014 17:35:41 +0200 Subject: [PATCH 66/90] Replaced unicode() function by six.text_type The Figure options dialog wasn' t working for me under Python3. Because ther is no unicode() function in Python3 I had to replace this by six.text_type. --- lib/matplotlib/backends/qt_editor/formlayout.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/backends/qt_editor/formlayout.py b/lib/matplotlib/backends/qt_editor/formlayout.py index 78836720b629..bc6d0a2db1e2 100644 --- a/lib/matplotlib/backends/qt_editor/formlayout.py +++ b/lib/matplotlib/backends/qt_editor/formlayout.py @@ -323,7 +323,7 @@ def get(self): elif tuple_to_qfont(value) is not None: value = field.get_font() elif isinstance(value, six.string_types) or is_color_like(value): - value = unicode(field.text()) + value = six.text_type(field.text()) elif isinstance(value, (list, tuple)): index = int(field.currentIndex()) if isinstance(value[0], (list, tuple)): From 926bc633ab5edaaa318989cffe97935e196941be Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 29 Jul 2014 12:19:51 -0400 Subject: [PATCH 67/90] MNT : bumped version on master branch to 1.5.x --- lib/matplotlib/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 25d7e04f3103..7d31f01f05f9 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -106,7 +106,7 @@ import sys import distutils.version -__version__ = '1.4.x' +__version__ = '1.5.x' __version__numpy__ = '1.6' # minimum required numpy version try: From d797bc18b4822098345d47212c4627f77173ae90 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Tue, 29 Jul 2014 12:27:07 -0400 Subject: [PATCH 68/90] Fix #3304. Fix crash in tostring_rgba_minimized when the region has zero-size. Thanks to @neothemachine for tracking this down. --- src/_backend_agg.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/_backend_agg.cpp b/src/_backend_agg.cpp index 416a3991f4ad..a392ce5c9f5e 100644 --- a/src/_backend_agg.cpp +++ b/src/_backend_agg.cpp @@ -2420,6 +2420,12 @@ RendererAgg::tostring_rgba_minimized(const Py::Tuple& args) *dst = src[y * width + x]; } } + } else { + data = PyBytes_FromStringAndSize(NULL, 0); + if (data == NULL) + { + throw Py::MemoryError("RendererAgg::tostring_minimized could not allocate memory"); + } } Py::Tuple bounds(4); From a501abb7385dd0dc7480e87b72dc540a27d7fcc2 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Tue, 29 Jul 2014 13:17:02 -0400 Subject: [PATCH 69/90] Fix exception message --- src/_backend_agg.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_backend_agg.cpp b/src/_backend_agg.cpp index a392ce5c9f5e..a141b6ab4a73 100644 --- a/src/_backend_agg.cpp +++ b/src/_backend_agg.cpp @@ -2408,7 +2408,7 @@ RendererAgg::tostring_rgba_minimized(const Py::Tuple& args) data = PyBytes_FromStringAndSize(NULL, newsize); if (data == NULL) { - throw Py::MemoryError("RendererAgg::tostring_minimized could not allocate memory"); + throw Py::MemoryError("RendererAgg::tostring_rgba_minimized could not allocate memory"); } dst = (unsigned int *)PyBytes_AsString(data); @@ -2424,7 +2424,7 @@ RendererAgg::tostring_rgba_minimized(const Py::Tuple& args) data = PyBytes_FromStringAndSize(NULL, 0); if (data == NULL) { - throw Py::MemoryError("RendererAgg::tostring_minimized could not allocate memory"); + throw Py::MemoryError("RendererAgg::tostring_rgba_minimized could not allocate memory"); } } From 5b0e0d86f080cd06cd4ad0fe0ca44f048abffedb Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Tue, 29 Jul 2014 13:17:10 -0400 Subject: [PATCH 70/90] Simplify reference counting --- src/_backend_agg.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/_backend_agg.cpp b/src/_backend_agg.cpp index a141b6ab4a73..b088c97c9aa3 100644 --- a/src/_backend_agg.cpp +++ b/src/_backend_agg.cpp @@ -2435,8 +2435,7 @@ RendererAgg::tostring_rgba_minimized(const Py::Tuple& args) bounds[3] = Py::Int(newheight); Py::Tuple result(2); - result[0] = Py::Object(data, false); - Py_DECREF(data); + result[0] = Py::Object(data, true); result[1] = bounds; return result; From 6ba32e89b20cf1fe7995c289ce5ceb89a68d862f Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 31 Jul 2014 17:15:43 -0400 Subject: [PATCH 71/90] DOC : added folders for api_changes and whats_new Added README + folders for whats_new and api_changes files. The idea is instead of everyone editing the same file (which leads to frequent conflicts requiring either manual merging or re-basing) each entry will create a new file which will be assembled into a single file during the release. --- doc/api/api_changes/README.txt | 9 +++++++++ doc/users/whats_new/README.txt | 14 ++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 doc/api/api_changes/README.txt create mode 100644 doc/users/whats_new/README.txt diff --git a/doc/api/api_changes/README.txt b/doc/api/api_changes/README.txt new file mode 100644 index 000000000000..199ce793cfc6 --- /dev/null +++ b/doc/api/api_changes/README.txt @@ -0,0 +1,9 @@ +For changes which require an entry in `api_changes.rst` please create a file +in this folder with the name YYYY-MM-DD-[initials].rst (ex 2014-07-31-TAC.rst) +with contents following the form: + +Brief destription of change +``````````````````````````` + +Long description of change, justification, and work-arounds to +maintain old behavior (if any). diff --git a/doc/users/whats_new/README.txt b/doc/users/whats_new/README.txt new file mode 100644 index 000000000000..dc0ea62eb26c --- /dev/null +++ b/doc/users/whats_new/README.txt @@ -0,0 +1,14 @@ +This folder is for placing new portions of `whats_new.rst`. + +When adding an entry please look at the currently existing files to see +if you can extend any of them. If you create a name name it `cool_new_feature.rst` +and include contentents of the form: + +Section Title for Feature +------------------------- + +A bunch of text about how awesome the new feature is and examples of how +to use it. + +A sub-section +````````````` From 96a3ea27cb0525704eacbe1b483a2479cdad19dc Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Wed, 6 Aug 2014 11:45:19 -0400 Subject: [PATCH 72/90] Reduce minimum freetype version to 2.3 --- setupext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setupext.py b/setupext.py index da25ab86fe55..026cdd5e707a 100755 --- a/setupext.py +++ b/setupext.py @@ -937,7 +937,7 @@ def check(self): return self._check_for_pkg_config( 'freetype2', 'ft2build.h', - min_version='2.4', version=version) + min_version='2.3', version=version) def add_flags(self, ext): pkg_config.setup_extension( From 811761a28d22cab55ce5c8716972b54339403d81 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 8 Aug 2014 10:33:31 -0400 Subject: [PATCH 73/90] DOC : tweaked api_change/whats_new README - fixed typos - changed txt -> rst so gh will display it nicely (maybe?) - minor prose change to be clearer --- doc/api/api_changes/README.rst | 9 +++++++++ doc/api/api_changes/README.txt | 9 --------- doc/users/whats_new/README.rst | 16 ++++++++++++++++ doc/users/whats_new/README.txt | 14 -------------- 4 files changed, 25 insertions(+), 23 deletions(-) create mode 100644 doc/api/api_changes/README.rst delete mode 100644 doc/api/api_changes/README.txt create mode 100644 doc/users/whats_new/README.rst delete mode 100644 doc/users/whats_new/README.txt diff --git a/doc/api/api_changes/README.rst b/doc/api/api_changes/README.rst new file mode 100644 index 000000000000..f317cab10d41 --- /dev/null +++ b/doc/api/api_changes/README.rst @@ -0,0 +1,9 @@ +For changes which require an entry in `api_changes.rst` please create +a file in this folder with the name :file:`YYYY-MM-DD-[initials].rst` +(ex :file:`2014-07-31-TAC.rst`) with contents following the form: :: + + Brief description of change + ``````````````````````````` + + Long description of change, justification, and work-arounds to + maintain old behavior (if any). diff --git a/doc/api/api_changes/README.txt b/doc/api/api_changes/README.txt deleted file mode 100644 index 199ce793cfc6..000000000000 --- a/doc/api/api_changes/README.txt +++ /dev/null @@ -1,9 +0,0 @@ -For changes which require an entry in `api_changes.rst` please create a file -in this folder with the name YYYY-MM-DD-[initials].rst (ex 2014-07-31-TAC.rst) -with contents following the form: - -Brief destription of change -``````````````````````````` - -Long description of change, justification, and work-arounds to -maintain old behavior (if any). diff --git a/doc/users/whats_new/README.rst b/doc/users/whats_new/README.rst new file mode 100644 index 000000000000..dd8e4a8a5214 --- /dev/null +++ b/doc/users/whats_new/README.rst @@ -0,0 +1,16 @@ +This folder is for placing new portions of `whats_new.rst`. + +When adding an entry please look at the currently existing files to +see if you can extend any of them. If you create a file, name it +something like :file:`cool_new_feature.rst` if you have added a brand new +feature or something like :file:`updated_feature.rst` for extensions of +existing features. Include contents of the form: :: + + Section Title for Feature + ------------------------- + + A bunch of text about how awesome the new feature is and examples of how + to use it. + + A sub-section + ````````````` diff --git a/doc/users/whats_new/README.txt b/doc/users/whats_new/README.txt deleted file mode 100644 index dc0ea62eb26c..000000000000 --- a/doc/users/whats_new/README.txt +++ /dev/null @@ -1,14 +0,0 @@ -This folder is for placing new portions of `whats_new.rst`. - -When adding an entry please look at the currently existing files to see -if you can extend any of them. If you create a name name it `cool_new_feature.rst` -and include contentents of the form: - -Section Title for Feature -------------------------- - -A bunch of text about how awesome the new feature is and examples of how -to use it. - -A sub-section -````````````` From 34737d95efb2b1ccafccda073dce18d0225fd9cb Mon Sep 17 00:00:00 2001 From: Martin Thoma Date: Sun, 10 Aug 2014 14:13:23 -0400 Subject: [PATCH 74/90] PEP8 conformity; removed outcommented code --- examples/user_interfaces/embedding_in_tk2.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/examples/user_interfaces/embedding_in_tk2.py b/examples/user_interfaces/embedding_in_tk2.py index 9e2a524a1b2d..c762873e5972 100644 --- a/examples/user_interfaces/embedding_in_tk2.py +++ b/examples/user_interfaces/embedding_in_tk2.py @@ -3,7 +3,7 @@ matplotlib.use('TkAgg') from numpy import arange, sin, pi -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.figure import Figure import sys @@ -12,19 +12,20 @@ else: import tkinter as Tk -def destroy(e): sys.exit() + +def destroy(e): + sys.exit() root = Tk.Tk() root.wm_title("Embedding in TK") -#root.bind("", destroy) -f = Figure(figsize=(5,4), dpi=100) +f = Figure(figsize=(5, 4), dpi=100) a = f.add_subplot(111) -t = arange(0.0,3.0,0.01) +t = arange(0.0, 3.0, 0.01) s = sin(2*pi*t) -a.plot(t,s) +a.plot(t, s) a.set_title('Tk embedding') a.set_xlabel('X axis label') a.set_ylabel('Y label') @@ -35,8 +36,6 @@ def destroy(e): sys.exit() canvas.show() canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) -#toolbar = NavigationToolbar2TkAgg( canvas, root ) -#toolbar.update() canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) button = Tk.Button(master=root, text='Quit', command=sys.exit) From 7bf81543943c94e4700af496582d1696361185e0 Mon Sep 17 00:00:00 2001 From: Eric Dill Date: Fri, 15 Aug 2014 15:09:21 -0400 Subject: [PATCH 75/90] DOC: Fixed the wording of the deprecation warning --- lib/matplotlib/backends/qt4_compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/backends/qt4_compat.py b/lib/matplotlib/backends/qt4_compat.py index e28a479c434d..b425e0290ba0 100644 --- a/lib/matplotlib/backends/qt4_compat.py +++ b/lib/matplotlib/backends/qt4_compat.py @@ -1,7 +1,7 @@ import warnings from matplotlib.cbook import mplDeprecation _warn_str = ("This module has been deprecated in 1.4 in favor " - "matplotlib.backends.qt_compat\n" + "of matplotlib.backends.qt_compat\n" "This module will be removed in 1.5, please update " "your imports.") # bulk-imports because we are pretending that file is this file From 481314e82efae04bda1cf3c4c3bfb5a6b1d0f569 Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Sun, 17 Aug 2014 16:09:53 -0700 Subject: [PATCH 76/90] BUG: Fixes custom path marker sizing for issue #1980 --- lib/matplotlib/markers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/markers.py b/lib/matplotlib/markers.py index a039f2246ee6..796712c90e4c 100644 --- a/lib/matplotlib/markers.py +++ b/lib/matplotlib/markers.py @@ -269,7 +269,7 @@ def _set_custom_marker(self, path): verts = path.vertices rescale = max(np.max(np.abs(verts[:, 0])), np.max(np.abs(verts[:, 1]))) - self._transform = Affine2D().scale(1.0 / rescale) + self._transform = Affine2D().scale(0.5 / rescale) self._path = path def _set_path_marker(self): From 34777445a434cb63df5b5f0f1c0abaaf35eab100 Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Sun, 17 Aug 2014 19:52:30 -0700 Subject: [PATCH 77/90] TST: Updated vertex_markers baseline image for marker size fix --- .../test_axes/vertex_markers.png | Bin 5167 -> 5063 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/lib/matplotlib/tests/baseline_images/test_axes/vertex_markers.png b/lib/matplotlib/tests/baseline_images/test_axes/vertex_markers.png index 538c07ec5aec67295c65d010661809c2cc24883c..49348ebe6ef627619a3afae8f48d8d0bc48d7e62 100644 GIT binary patch literal 5063 zcmeI0dsLJ68OMJK5duvGN6TepNb6}GLs5|yu)$b`DI(A#W)USpIwOk|jB-r_A<`pa z^#I+JOkiPE0gVt8Nw@?_C{sEzmJKYK1V|#6=+c)2kPygiF9z^h?Bp_8p4CkUViazrx>I2nvOn>hFHO5!gS-dXq$r#;c{`sFJ(X-d}wk>Xt(+a9@ zl4FXI061xDd3NxZZaNDP0DOc(!$<3O0KjxZz@PpJ?F4|=FPZQKTaYLK^4E9)u;ar2 zFP*OdSL!JeW*c-hS!QEX3&Ygh-``&yYzLTeSr+r%EYmEnCELJn`DE%i6qFFgEGv?f z<5-Ssw^s+-0H$z!e5hl>Y%=f?%HBqt+DQ^JhAxnoZ! zWSX(9;L1l7mER zq~)n3Td^nDax#@@E}wieb-9!wyd;4R#r|q5Hixa8nZ!s+RUP3Oybah~-4I(F%oO*gWxMB@6*Qv!;z{Fcu_$1q5T%#m-Bz=uX~g>{ zF}{y3+B>oFcpkZ=Z(VJss|*RSg&a<&e?+20v9B?A)enz=(VfKp8arJP-)3CotVS}ijvY)3~&pPD8Q$`vyh0lb@A0sBIsp=w#t!fChks}DYVPSN;zn^@b#U%(+}LYl z>F<{#5Fqe_!P?+I_#9l3G=sSV%J|EaP;{pf33>5>nIvhNd&ibWbY>(H709tt!Z#X^ zCzn+662P0>BQWfLi0CP&A<~^@S?GbSqw(=m-?FG-6!d z7dW_)1IQk@Rj#I(rOZYuU~fWIayT1%9(dC33I0*6`Vf!5TuFi4fsth;!uR_aGiHHX?b`p<;3j9aX3^WIs%9AUDV^&5_3g0EQA?fK4x2 z2rs978vO)90BkK+-MJYvheY3@K->AMF6njP0oThnc7YMtQqFO)KW{f_+?T(KDO4?K z#mK)63=o^{-Zec*NRk!vgVXQ9T*uwe7`mr%IrqO&D&s5OW$j{?{n21B%(DRjv{y`H zt|Eh}^<%iEE<{@^O28DSDy;}J3j2^}>L3)Uu|F1c|7EJr2J}8iBf94)=LVPT(V=)t zAGHiPz)}Vfr1}M>Ft0vuau;URsTX+${ni%Id22Rk>687e${=&F7f?Mq9#c$))?oYQ z*JtrSGGUH7aL~}7nNEzd7Wk#Z!oQCmzgNTQrK(2V?1yP@n{4Ka0}I+hMHNq)TQR$G zv@P5doq-GoUnKi?08zloAI3JpORyjo^vTGH!1aOktWu68VoW9K0S~cNvi*e@K*|TRa^~SD7oXQ>3XvD|v#L#Vl=)6u42Q>43nQ$o8 z-HJd%4ldLOSV;w?WX>SjNdfCOiwtk{SX`rp@1GG2{51g%U6DmATM1Egj%ayvXdtOMrdj@&YPOvxa|U{he}G2Ye4Nb=NRuYnB!?Kl+^=nZLs0FHuT**)Zj=KF9W% z9_yhd+*(d;T~z6F!+mGte{Z|@p1KoY;$+l1AL!2`u&)nz+qq3aAIUqB^jAg zOGidWpT)^XhH1@+T3R~-$r?t!zDY=w1clENbNsuJe?{h<|DmfIwE!2yZ?EbDj%r~= OL0D*H2zTd^+Iv zLI3tFykAY)c_bGAr{m@y>RNbXDgf)B#6v)${) z?X#x_FrP(}ZHGQ~y+q$Ierz)*BF*+@jLXG)1ku{gGh1~0+;<9&ozom_Vm1cOV`L~N zNBtb>T5Vz7hz$U1)#2$*@Vmo~D0uf3dMyB6@6+MH78^SN&N_Pm5K#e80DNeNhEKm0 z0RVl&kB?du7~+J|7xZIXUVk40D{u;*Kk&=iM`Bn#{X-dTfnb`~2&Xi{@ahTPLWycy zd<;GDA_E&?)k|pWYvCyggNur8Jqk za5&tbLq85^EklGsoh2MaUapZNfiFxN=Ft{tx{S`Npv_B0)e+upxWX)%yMD4oQi)K0 zQczI9)rL!sD%_L85%pYK8S-eRcw?BfWirR){4481)@uz$@%oTY)5*rfp0N@4q+o>6 zGM-X5QmAsqZS!Oh;Y)iIa%>|CBzo!6M>Gp7(;=CNCx1-TOc*tcKh@;jeYZz_ygM53 zMdD^(s*FkL;*4!|=qWwl`i{*f(fGe~RVYmr`^hbTa{+S6H8#6K*{XHmT-kB+m0A3u zN^DCeu&#UW9rzr6Js875fvz*DHM<4^H!-{395a!d0rtcOtXN($5P-;T;@eMACcGb~z zK4yuqe*5uwqDSAI6a4#AXcueWEuGt>`!MDdjg~wC~d@C`H_91xXzWDd_sZD4xZY+OHT$nv23G zrb;-|)Pkwm_TekX^*7}|lix{|G*44$aD*dx&jI6SbJ-59-h)>OLdJ#qPYTXwppqKd z?Ea7o=|T|q?Q&xM0N&?zXo0+G&*AtQ)~}(CSuTmTo$n6>R;_LYqwfW+tUONKIT^8BmqCvJDSA zEua(cWW>*PS-jQ%9wmYFq{)iwfV>)IqwbIW5811-zF@PrUQj~WLN2{xu!DrZ2Ie)S zwtp%_%!&$XS=R&i=9<@?YoaZGxAdU~#|>0GQdl{4mBb z7^zlB#A+iH10YCSij5EAB>4>!9p=fg9D2&_T~X?FHg@vL>(BdSMHY*#C3@&iQ0{fy zZwBfd&u$nWUzQP*dWuhvPfQ4;Qt2(GN@r-2F*V{C0Fvs*+C_OnUcq;hli3YH+;Y$s z;pF5*?2vL`NRsb0h_G}<-$#S0d~e^PjmrYKv&>aq`PrgU-0DpI$F6}>$qV;s2a#HP z1gH-MYjDDJhWzVB-7c)-8!}rPKsr6d{am=*Li+#YVsmH!m#wTV=UJKGnV$o>&U!(c zc^*40xi_2EyQ%Z&Neo}KtWG~5&BIg$E(Y#BoLckD-0J;XS9P7V(B$ipv7%dYcRKO) z@;d2=mAG_-mC+&6NzL;)T&92tk^VNy26q^bck9bmx$(W@77?C~TXyYo6?Y?aI^Ex4 zkQo$e4b;I2VIlFS0Qf)2BaF6>$c||A`c_d5vYnpz`||SgCTS*8hXQIAb<~Of92b=? zcOCzW6@|eCEG`TAcb2G0HM%ZDTl()GUlfKp>`e{vpg5rc-#%vr;wQ1TIb*j>XAZ^f z4m~i&0KXV3{&W+az0{F-d}X!=REvLx&8E8b8Qqxb%rAXO=U%nnk(3&iDDV+3sXJPitBF>k!(Pw2-Jg zLW|b!@zX_nbmfipUQx4Hsd*Fr?~S{8aB0LdXKj;`Ni)G+|CC{Z$L)M7DWp(*`jn7Y zSGz4I1v9y8xV2*i(lBYG(WsMo`?n>%ux%F47zfV}cv=Sly1qq{X}-MqDS!ppCS?j)HRReg%MDL5@L>?>s7otlgjQ{`u From edeba33881f8b6257c958bf36c589d13d91e2075 Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Mon, 18 Aug 2014 09:35:08 -0700 Subject: [PATCH 78/90] DOC: Elaborated marker style documentation for custom-defined markers --- lib/matplotlib/markers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/matplotlib/markers.py b/lib/matplotlib/markers.py index 796712c90e4c..b038d7ae8f53 100644 --- a/lib/matplotlib/markers.py +++ b/lib/matplotlib/markers.py @@ -45,6 +45,8 @@ "" nothing ``'$...$'`` render the string using mathtext. `verts` a list of (x, y) pairs used for Path vertices. + The center of the marker is located at (0,0) and + the size is normalized. path a `~matplotlib.path.Path` instance. (`numsides`, `style`, `angle`) see below ============================== =============================================== From f27b1809908f6c0240ec1a9462f228a72091a74e Mon Sep 17 00:00:00 2001 From: Christoph Gohlke Date: Wed, 20 Aug 2014 15:18:21 -0700 Subject: [PATCH 79/90] Move stylelib to mpl-data Fixes gh-3385. --- lib/matplotlib/{style => mpl-data}/stylelib/bmh.mplstyle | 0 .../{style => mpl-data}/stylelib/dark_background.mplstyle | 0 .../{style => mpl-data}/stylelib/fivethirtyeight.mplstyle | 0 lib/matplotlib/{style => mpl-data}/stylelib/ggplot.mplstyle | 0 lib/matplotlib/{style => mpl-data}/stylelib/grayscale.mplstyle | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename lib/matplotlib/{style => mpl-data}/stylelib/bmh.mplstyle (100%) rename lib/matplotlib/{style => mpl-data}/stylelib/dark_background.mplstyle (100%) rename lib/matplotlib/{style => mpl-data}/stylelib/fivethirtyeight.mplstyle (100%) rename lib/matplotlib/{style => mpl-data}/stylelib/ggplot.mplstyle (100%) rename lib/matplotlib/{style => mpl-data}/stylelib/grayscale.mplstyle (100%) diff --git a/lib/matplotlib/style/stylelib/bmh.mplstyle b/lib/matplotlib/mpl-data/stylelib/bmh.mplstyle similarity index 100% rename from lib/matplotlib/style/stylelib/bmh.mplstyle rename to lib/matplotlib/mpl-data/stylelib/bmh.mplstyle diff --git a/lib/matplotlib/style/stylelib/dark_background.mplstyle b/lib/matplotlib/mpl-data/stylelib/dark_background.mplstyle similarity index 100% rename from lib/matplotlib/style/stylelib/dark_background.mplstyle rename to lib/matplotlib/mpl-data/stylelib/dark_background.mplstyle diff --git a/lib/matplotlib/style/stylelib/fivethirtyeight.mplstyle b/lib/matplotlib/mpl-data/stylelib/fivethirtyeight.mplstyle similarity index 100% rename from lib/matplotlib/style/stylelib/fivethirtyeight.mplstyle rename to lib/matplotlib/mpl-data/stylelib/fivethirtyeight.mplstyle diff --git a/lib/matplotlib/style/stylelib/ggplot.mplstyle b/lib/matplotlib/mpl-data/stylelib/ggplot.mplstyle similarity index 100% rename from lib/matplotlib/style/stylelib/ggplot.mplstyle rename to lib/matplotlib/mpl-data/stylelib/ggplot.mplstyle diff --git a/lib/matplotlib/style/stylelib/grayscale.mplstyle b/lib/matplotlib/mpl-data/stylelib/grayscale.mplstyle similarity index 100% rename from lib/matplotlib/style/stylelib/grayscale.mplstyle rename to lib/matplotlib/mpl-data/stylelib/grayscale.mplstyle From 31b63f9ee95e080726711c914bef6e30b27eba31 Mon Sep 17 00:00:00 2001 From: cgohlke Date: Wed, 20 Aug 2014 15:21:41 -0700 Subject: [PATCH 80/90] Load styles from mpl-data --- lib/matplotlib/style/core.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index 02efe1f8a691..8bd0d13b44ed 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -27,8 +27,7 @@ __all__ = ['use', 'context', 'available', 'library', 'reload_library'] -_here = os.path.abspath(os.path.dirname(__file__)) -BASE_LIBRARY_PATH = os.path.join(_here, 'stylelib') +BASE_LIBRARY_PATH = os.path.join(mpl.get_data_path(), 'stylelib') # Users may want multiple library paths, so store a list of paths. USER_LIBRARY_PATHS = [os.path.join(mpl._get_configdir(), 'stylelib')] STYLE_EXTENSION = 'mplstyle' From 7afa906b4b268cd3a955ffba8fa11205909a68a6 Mon Sep 17 00:00:00 2001 From: cgohlke Date: Wed, 20 Aug 2014 15:51:35 -0700 Subject: [PATCH 81/90] Include stylelib files in MANIFEST.in --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 92f11d0dd7fe..4cda2f74bbf4 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -9,6 +9,7 @@ include lib/matplotlib/mpl-data/images/* include lib/matplotlib/mpl-data/fonts/ttf/* include lib/matplotlib/mpl-data/fonts/pdfcorefonts/* include lib/matplotlib/mpl-data/fonts/afm/* +include lib/matplotlib/mpl-data/stylelib/* recursive-include lib/matplotlib/mpl-data/sample_data * recursive-include LICENSE * recursive-include examples * From d4abb3745451b014c83bdabd31895d4c97d4c45b Mon Sep 17 00:00:00 2001 From: cgohlke Date: Wed, 20 Aug 2014 16:18:47 -0700 Subject: [PATCH 82/90] Include stylelib files in package_data --- setupext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setupext.py b/setupext.py index 48c4808e1923..210329d58ac3 100755 --- a/setupext.py +++ b/setupext.py @@ -585,7 +585,7 @@ def get_package_data(self): 'backends/web_backend/jquery/css/themes/base/images/*', 'backends/web_backend/css/*.*', 'backends/Matplotlib.nib/*', - 'style/stylelib/*.mplstyle', + 'mpl-data/stylelib/*.mplstyle', ]} From f4782dad1902e9d6398607e85c714607fe9aaa47 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 22 Aug 2014 12:52:49 -0400 Subject: [PATCH 83/90] DOC : add note about np.matrix and pandas objects There is no intentional support for np.matrix or pandas data objects as input to plotting functions. --- doc/faq/usage_faq.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/faq/usage_faq.rst b/doc/faq/usage_faq.rst index 26c00198dfcc..832eb5482647 100644 --- a/doc/faq/usage_faq.rst +++ b/doc/faq/usage_faq.rst @@ -114,6 +114,15 @@ idea). When the figure is rendered, all of the artists are drawn to the **canvas**. Most Artists are tied to an Axes; such an Artist cannot be shared by multiple Axes, or moved from one to another. +.. _input_types: + +Types of inputs to plotting functions +===================================== + +All of plotting functions expect `np.array` or `np.ma.masked_array` as +input. Classes that are 'array-like' such as `pandas` data objects +and `np.matrix` may or may not work as intended. It is best to +convert these to `np.array` objects prior to plotting. .. _pylab: From 676f04b4c5293a037da70d3ce348032e29c00b4f Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 22 Aug 2014 13:32:10 -0400 Subject: [PATCH 84/90] DOC : add examples of casting DataFrame and matrix --- doc/faq/usage_faq.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/faq/usage_faq.rst b/doc/faq/usage_faq.rst index 832eb5482647..1dcd7f7b7cc9 100644 --- a/doc/faq/usage_faq.rst +++ b/doc/faq/usage_faq.rst @@ -124,6 +124,18 @@ input. Classes that are 'array-like' such as `pandas` data objects and `np.matrix` may or may not work as intended. It is best to convert these to `np.array` objects prior to plotting. +For example, to covert a `pandas.DataFrame` :: + + a = pandas.DataFrame(np.random.rand(4,5), columns = list('abcde')) + a_asndarray = a.values + +and to covert a `np.matrix` :: + + b = np.matrix([[1,2],[3,4]]) + b_asarray = np.asarray(b) + + + .. _pylab: Matplotlib, pyplot and pylab: how are they related? From 549998082104e8f1aa395cb77a7b0111f54cc28c Mon Sep 17 00:00:00 2001 From: Werner F Bruhin Date: Tue, 11 Feb 2014 16:49:16 +0100 Subject: [PATCH 85/90] - get wx backends compatible with wxPython Phoenix --- lib/matplotlib/backends/backend_wx.py | 71 +++++++++++++++++------- lib/matplotlib/backends/backend_wxagg.py | 26 +++++++-- 2 files changed, 71 insertions(+), 26 deletions(-) diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index d99f04f07ea9..8a9043a3a18f 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -667,8 +667,6 @@ class FigureCanvasWx(FigureCanvasBase, wx.Panel): wx.WXK_DELETE : 'delete', wx.WXK_HOME : 'home', wx.WXK_END : 'end', - wx.WXK_PRIOR : 'pageup', - wx.WXK_NEXT : 'pagedown', wx.WXK_PAGEUP : 'pageup', wx.WXK_PAGEDOWN : 'pagedown', wx.WXK_NUMPAD0 : '0', @@ -691,8 +689,6 @@ class FigureCanvasWx(FigureCanvasBase, wx.Panel): wx.WXK_NUMPAD_RIGHT : 'right', wx.WXK_NUMPAD_DOWN : 'down', wx.WXK_NUMPAD_LEFT : 'left', - wx.WXK_NUMPAD_PRIOR : 'pageup', - wx.WXK_NUMPAD_NEXT : 'pagedown', wx.WXK_NUMPAD_PAGEUP : 'pageup', wx.WXK_NUMPAD_PAGEDOWN : 'pagedown', wx.WXK_NUMPAD_HOME : 'home', @@ -736,7 +732,10 @@ def do_nothing(*args, **kwargs): # Create the drawing bitmap - self.bitmap =wx.EmptyBitmap(w, h) + if 'phoenix' in wx.PlatformInfo: + self.bitmap =wx.Bitmap(w, h) + else: + self.bitmap =wx.EmptyBitmap(w, h) DEBUG_MSG("__init__() - bitmap w:%d h:%d" % (w,h), 2, self) # TODO: Add support for 'point' inspection and plot navigation. self._isDrawn = False @@ -873,7 +872,10 @@ def start_event_loop(self, timeout=0): bind(self, wx.EVT_TIMER, self.stop_event_loop, id=id) # Event loop handler for start/stop event loop - self._event_loop = wx.EventLoop() + if 'phoenix' in wx.PlatformInfo: + self._event_loop = wx.GUIEventLoop() + else: + self._event_loop = wx.EventLoop() self._event_loop.Run() timer.Stop() @@ -897,7 +899,7 @@ def _get_imagesave_wildcards(self): 'return the wildcard string for the filesave dialog' default_filetype = self.get_default_filetype() filetypes = self.get_supported_filetypes_grouped() - sorted_filetypes = list(six.iteritems(filetypes)) + sorted_filetypes = filetypes.items() sorted_filetypes.sort() wildcards = [] extensions = [] @@ -923,9 +925,12 @@ def gui_repaint(self, drawDC=None): if drawDC is None: drawDC=wx.ClientDC(self) - drawDC.BeginDrawing() - drawDC.DrawBitmap(self.bitmap, 0, 0) - drawDC.EndDrawing() + if 'phoenix' in wx.PlatformInfo: + drawDC.DrawBitmap(self.bitmap, 0, 0) + else: + drawDC.BeginDrawing() + drawDC.DrawBitmap(self.bitmap, 0, 0) + drawDC.EndDrawing() #wx.GetApp().Yield() else: pass @@ -979,7 +984,11 @@ def _print_image(self, filename, filetype, *args, **kwargs): width = int(math.ceil(width)) height = int(math.ceil(height)) - self.bitmap = wx.EmptyBitmap(width, height) + if 'phoenix' in wx.PlatformInfo: + self.bitmap =wx.Bitmap(width, height) + else: + self.bitmap =wx.EmptyBitmap(width, height) + renderer = RendererWx(self.bitmap, self.figure.dpi) gc = renderer.new_gc() @@ -1052,7 +1061,11 @@ def _onSize(self, evt): DEBUG_MSG("_onSize()", 2, self) # Create a new, correctly sized bitmap self._width, self._height = self.GetClientSize() - self.bitmap =wx.EmptyBitmap(self._width, self._height) + if 'phoenix' in wx.PlatformInfo: + self.bitmap =wx.Bitmap(self._width, self._height) + else: + self.bitmap =wx.EmptyBitmap(self._width, self._height) + self._isDrawn = False if self._width <= 1 or self._height <= 1: return # Empty figure @@ -1636,12 +1649,25 @@ def _init_toolbar(self): self.AddSeparator() continue self.wx_ids[text] = wx.NewId() - if text in ['Pan', 'Zoom']: - self.AddCheckTool(self.wx_ids[text], _load_bitmap(image_file + '.png'), - shortHelp=text, longHelp=tooltip_text) + if 'phoenix' in wx.PlatformInfo: + if text in ['Pan', 'Zoom']: + kind = wx.ITEM_CHECK + else: + kind = wx.ITEM_NORMAL + self.AddTool(self.wx_ids[text], label=text, + bitmap=_load_bitmap(image_file + '.png'), + bmpDisabled=wx.NullBitmap, + shortHelpString=text, + longHelpString=tooltip_text, + kind=kind) else: - self.AddSimpleTool(self.wx_ids[text], _load_bitmap(image_file + '.png'), - text, tooltip_text) + if text in ['Pan', 'Zoom']: + self.AddCheckTool(self.wx_ids[text], _load_bitmap(image_file + '.png'), + shortHelp=text, longHelp=tooltip_text) + else: + self.AddSimpleTool(self.wx_ids[text], _load_bitmap(image_file + '.png'), + text, tooltip_text) + bind(self, wx.EVT_TOOL, getattr(self, callback), id=self.wx_ids[text]) self.Realize() @@ -1700,7 +1726,10 @@ def save_figure(self, *args): error_msg_wx(str(e)) def set_cursor(self, cursor): - cursor =wx.StockCursor(cursord[cursor]) + if 'phoenix' in wx.PlatformInfo: + cursor = wx.Cursor(cursord[cursor]) + else: + cursor = wx.StockCursor(cursord[cursor]) self.canvas.SetCursor( cursor ) def release(self, event): @@ -1737,7 +1766,8 @@ def draw_rubberband(self, event, x0, y0, x1, y1): dc.ResetBoundingBox() - dc.BeginDrawing() + if not 'phoenix' in wx.PlatformInfo: + dc.BeginDrawing() height = self.canvas.figure.bbox.height y1 = height - y1 y0 = height - y0 @@ -1754,7 +1784,8 @@ def draw_rubberband(self, event, x0, y0, x1, y1): else: dc.DrawRectangle(*lastrect) #erase last self.lastrect = rect dc.DrawRectangle(*rect) - dc.EndDrawing() + if not 'phoenix' in wx.PlatformInfo: + dc.EndDrawing() def set_status_bar(self, statbar): self.statbar = statbar diff --git a/lib/matplotlib/backends/backend_wxagg.py b/lib/matplotlib/backends/backend_wxagg.py index 5dd01030560b..c3dbea202b54 100644 --- a/lib/matplotlib/backends/backend_wxagg.py +++ b/lib/matplotlib/backends/backend_wxagg.py @@ -136,7 +136,10 @@ def _convert_agg_to_wx_image(agg, bbox): """ if bbox is None: # agg => rgb -> image - image = wx.EmptyImage(int(agg.width), int(agg.height)) + if 'phoenix' in wx.PlatformInfo: + image = wx.Image(int(agg.width), int(agg.height)) + else: + image = wx.EmptyImage(int(agg.width), int(agg.height)) image.SetData(agg.tostring_rgb()) return image else: @@ -153,8 +156,12 @@ def _convert_agg_to_wx_bitmap(agg, bbox): """ if bbox is None: # agg => rgba buffer -> bitmap - return wx.BitmapFromBufferRGBA(int(agg.width), int(agg.height), - agg.buffer_rgba()) + if 'phoenix' in wx.PlatformInfo: + return wx.Bitmap.FromBufferRGBA(int(agg.width), int(agg.height), + agg.buffer_rgba()) + else: + return wx.BitmapFromBufferRGBA(int(agg.width), int(agg.height), + agg.buffer_rgba()) else: # agg => rgba buffer -> bitmap => clipped bitmap return _WX28_clipped_agg_as_bitmap(agg, bbox) @@ -170,12 +177,19 @@ def _WX28_clipped_agg_as_bitmap(agg, bbox): r = l + width t = b + height - srcBmp = wx.BitmapFromBufferRGBA(int(agg.width), int(agg.height), - agg.buffer_rgba()) + if 'phoenix' in wx.PlatformInfo: + srcBmp = wx.Bitmap.FromBufferRGBA(int(agg.width), int(agg.height), + agg.buffer_rgba()) + else: + srcBmp = wx.BitmapFromBufferRGBA(int(agg.width), int(agg.height), + agg.buffer_rgba()) srcDC = wx.MemoryDC() srcDC.SelectObject(srcBmp) - destBmp = wx.EmptyBitmap(int(width), int(height)) + if 'phoenix' in wx.PlatformInfo: + destBmp = wx.Bitmap(int(width), int(height)) + else: + destBmp = wx.EmptyBitmap(int(width), int(height)) destDC = wx.MemoryDC() destDC.SelectObject(destBmp) From 6acf934dd739d7e91afc65ee7c1dfd4750f86bc7 Mon Sep 17 00:00:00 2001 From: Werner F Bruhin Date: Tue, 11 Feb 2014 16:49:40 +0100 Subject: [PATCH 86/90] - get wx examples compatible with wxPython Phoenix --- examples/user_interfaces/embedding_in_wx2.py | 26 +++++++++-- examples/user_interfaces/embedding_in_wx3.py | 29 ++++++++++-- examples/user_interfaces/embedding_in_wx4.py | 33 ++++++++++---- examples/user_interfaces/embedding_in_wx5.py | 31 ++++++++++--- examples/user_interfaces/fourier_demo_wx.py | 48 +++++++++++++------- examples/user_interfaces/wxcursor_demo.py | 26 +++++++++-- 6 files changed, 149 insertions(+), 44 deletions(-) diff --git a/examples/user_interfaces/embedding_in_wx2.py b/examples/user_interfaces/embedding_in_wx2.py index 2fe9d09376b6..4d5e4ec1554b 100644 --- a/examples/user_interfaces/embedding_in_wx2.py +++ b/examples/user_interfaces/embedding_in_wx2.py @@ -6,7 +6,10 @@ # Used to guarantee to use at least Wx2.8 import wxversion -wxversion.ensureMinimal('2.8') +#wxversion.ensureMinimal('2.8') +wxversion.select('2.8') +#wxversion.select('2.9.5') # 2.9.x classic +#wxversion.select('3.0.1-msw-phoenix', optionsRequired=True) # 3.x Phoenix from numpy import arange, sin, pi @@ -25,6 +28,10 @@ from matplotlib.figure import Figure import wx +print wx.VERSION_STRING +print wx.PlatformInfo +print matplotlib.__version__ + class CanvasFrame(wx.Frame): @@ -32,7 +39,10 @@ def __init__(self): wx.Frame.__init__(self,None,-1, 'CanvasFrame',size=(550,350)) - self.SetBackgroundColour(wx.NamedColour("WHITE")) + if 'phoenix' in wx.PlatformInfo: + self.SetBackgroundColour(wx.Colour("WHITE")) + else: + self.SetBackgroundColour(wx.NamedColour("WHITE")) self.figure = Figure() self.axes = self.figure.add_subplot(111) @@ -48,7 +58,11 @@ def __init__(self): self.Fit() self.add_toolbar() # comment this out for no toolbar + self.Bind(wx.EVT_PAINT, self.OnPaint) + def OnPaint(self, event): + self.canvas.draw() + event.Skip() def add_toolbar(self): self.toolbar = NavigationToolbar2Wx(self.canvas) @@ -61,8 +75,12 @@ def add_toolbar(self): else: # On Windows platform, default window size is incorrect, so set # toolbar width to figure width. - tw, th = self.toolbar.GetSizeTuple() - fw, fh = self.canvas.GetSizeTuple() + if 'phoenix' in wx.PlatformInfo: + tw, th = self.toolbar.GetSize() + fw, fh = self.canvas.GetSize() + else: + tw, th = self.toolbar.GetSizeTuple() + fw, fh = self.canvas.GetSizeTuple() # By adding toolbar in sizer, we are able to put it at the bottom # of the frame - so appearance is closer to GTK version. # As noted above, doesn't work for Mac. diff --git a/examples/user_interfaces/embedding_in_wx3.py b/examples/user_interfaces/embedding_in_wx3.py index 931e043a5fd3..0f4060dd411d 100755 --- a/examples/user_interfaces/embedding_in_wx3.py +++ b/examples/user_interfaces/embedding_in_wx3.py @@ -23,7 +23,10 @@ # Used to guarantee to use at least Wx2.8 import wxversion -wxversion.ensureMinimal('2.8') +#wxversion.ensureMinimal('2.8') +#wxversion.select('2.8') +#wxversion.select('2.9.5') # 2.9.x classic +wxversion.select('3.0.1-msw-phoenix', optionsRequired=True) # 3.0 phoenix import sys, time, os, gc import matplotlib @@ -35,6 +38,9 @@ import numpy as np import wx + +print(wx.VERSION_STRING) + import wx.xrc as xrc ERR_TOL = 1e-5 # floating point slop for peak-detection @@ -61,6 +67,11 @@ def __init__(self, parent): sizer.Add(self.toolbar, 0, wx.GROW) self.SetSizer(sizer) self.Fit() + self.Bind(wx.EVT_PAINT, self.OnPaint) + + def OnPaint(self, event): + self.canvas.draw() + event.Skip() def init_plot_data(self): a = self.fig.add_subplot(111) @@ -132,14 +143,22 @@ def OnInit(self): # whiz button ------------------ whiz_button = xrc.XRCCTRL(self.frame,"whiz_button") - wx.EVT_BUTTON(whiz_button, whiz_button.GetId(), - self.plotpanel.OnWhiz) + + if 'phoenix' in wx.PlatformInfo: + whiz_button.Bind(wx.EVT_BUTTON, self.plotpanel.OnWhiz) + else: + wx.EVT_BUTTON(whiz_button, whiz_button.GetId(), + self.plotpanel.OnWhiz) # bang button ------------------ bang_button = xrc.XRCCTRL(self.frame,"bang_button") - wx.EVT_BUTTON(bang_button, bang_button.GetId(), - self.OnBang) + if 'phoenix' in wx.PlatformInfo: + bang_button.Bind(wx.EVT_BUTTON, self.OnBang) + + else: + wx.EVT_BUTTON(bang_button, bang_button.GetId(), + self.OnBang) # final setup ------------------ diff --git a/examples/user_interfaces/embedding_in_wx4.py b/examples/user_interfaces/embedding_in_wx4.py index 578be3b5da13..f982ea8a1321 100644 --- a/examples/user_interfaces/embedding_in_wx4.py +++ b/examples/user_interfaces/embedding_in_wx4.py @@ -6,7 +6,11 @@ # Used to guarantee to use at least Wx2.8 import wxversion -wxversion.ensureMinimal('2.8') +#wxversion.ensureMinimal('2.8') +wxversion.select('2.8') +#wxversion.select('2.9.5') # 2.9.x classic +#wxversion.select('3.0.1-msw-phoenix', optionsRequired=True) # 3.0 phoenix + from numpy import arange, sin, pi @@ -32,9 +36,15 @@ def __init__(self, canvas, cankill): # for simplicity I'm going to reuse a bitmap from wx, you'll # probably want to add your own. - self.AddSimpleTool(self.ON_CUSTOM, _load_bitmap('stock_left.xpm'), - 'Click me', 'Activate custom contol') - wx.EVT_TOOL(self, self.ON_CUSTOM, self._on_custom) + if 'phoenix' in wx.PlatformInfo: + self.AddTool(self.ON_CUSTOM, 'Click me', + _load_bitmap('stock_left.xpm'), + 'Activate custom contol') + self.Bind(wx.EVT_TOOL, self._on_custom, id=self.ON_CUSTOM) + else: + self.AddSimpleTool(self.ON_CUSTOM, _load_bitmap('stock_left.xpm'), + 'Click me', 'Activate custom contol') + wx.EVT_TOOL(self, self.ON_CUSTOM, self._on_custom) def _on_custom(self, evt): # add some text to the axes in a random location in axes (0,1) @@ -61,7 +71,10 @@ def __init__(self): wx.Frame.__init__(self,None,-1, 'CanvasFrame',size=(550,350)) - self.SetBackgroundColour(wx.NamedColour("WHITE")) + if 'phoenix' in wx.PlatformInfo: + self.SetBackgroundColour(wx.Colour("WHITE")) + else: + self.SetBackgroundColour(wx.NamedColour("WHITE")) self.figure = Figure(figsize=(5,4), dpi=100) self.axes = self.figure.add_subplot(111) @@ -75,7 +88,7 @@ def __init__(self): self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add(self.canvas, 1, wx.TOP | wx.LEFT | wx.EXPAND) # Capture the paint message - wx.EVT_PAINT(self, self.OnPaint) + self.Bind(wx.EVT_PAINT, self.OnPaint) self.toolbar = MyNavigationToolbar(self.canvas, True) self.toolbar.Realize() @@ -87,8 +100,12 @@ def __init__(self): else: # On Windows platform, default window size is incorrect, so set # toolbar width to figure width. - tw, th = self.toolbar.GetSizeTuple() - fw, fh = self.canvas.GetSizeTuple() + if 'phoenix' in wx.PlatformInfo: + tw, th = self.toolbar.GetSize() + fw, fh = self.canvas.GetSize() + else: + tw, th = self.toolbar.GetSizeTuple() + fw, fh = self.canvas.GetSizeTuple() # By adding toolbar in sizer, we are able to put it at the bottom # of the frame - so appearance is closer to GTK version. # As noted above, doesn't work for Mac. diff --git a/examples/user_interfaces/embedding_in_wx5.py b/examples/user_interfaces/embedding_in_wx5.py index fd3969aca881..ec4f7ebff7e0 100644 --- a/examples/user_interfaces/embedding_in_wx5.py +++ b/examples/user_interfaces/embedding_in_wx5.py @@ -1,9 +1,19 @@ # Used to guarantee to use at least Wx2.8 import wxversion -wxversion.ensureMinimal('2.8') +#wxversion.ensureMinimal('2.8') +wxversion.select('2.8') +#wxversion.select('2.9.5') # 2.9.x classic +#wxversion.select('3.0.1-msw-phoenix', optionsRequired=True) # 3.0 phoenix + import wx -import wx.aui +print wx.VERSION_STRING + +if 'phoenix' in wx.PlatformInfo: + import wx.lib.agw.aui as aui +else: + import wx.aui as aui + import matplotlib as mpl from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas from matplotlib.backends.backend_wxagg import NavigationToolbar2Wx as Toolbar @@ -20,23 +30,30 @@ def __init__(self, parent, id = -1, dpi = None, **kwargs): sizer.Add(self.canvas,1,wx.EXPAND) sizer.Add(self.toolbar, 0 , wx.LEFT | wx.EXPAND) self.SetSizer(sizer) + self.Bind(wx.EVT_PAINT, self.OnPaint) + + def OnPaint(self, event): + self.canvas.draw() + event.Skip() + class PlotNotebook(wx.Panel): def __init__(self, parent, id = -1): wx.Panel.__init__(self, parent, id=id) - self.nb = wx.aui.AuiNotebook(self) + self.nb = aui.AuiNotebook(self) sizer = wx.BoxSizer() sizer.Add(self.nb, 1, wx.EXPAND) self.SetSizer(sizer) def add(self,name="plot"): - page = Plot(self.nb) - self.nb.AddPage(page,name) - return page.figure + page = Plot(self.nb) + self.nb.AddPage(page,name) + return page.figure def demo(): - app = wx.PySimpleApp() + import wx.lib.mixins.inspection as wit + app = wit.InspectableApp() frame = wx.Frame(None,-1,'Plotter') plotter = PlotNotebook(frame) axes1 = plotter.add('figure 1').gca() diff --git a/examples/user_interfaces/fourier_demo_wx.py b/examples/user_interfaces/fourier_demo_wx.py index aa52809ff1c0..1d2ee7971322 100644 --- a/examples/user_interfaces/fourier_demo_wx.py +++ b/examples/user_interfaces/fourier_demo_wx.py @@ -1,6 +1,15 @@ import numpy as np -import wx +# Used to guarantee to use at least Wx2.8 +import wxversion +#wxversion.ensureMinimal('2.8') +wxversion.select('2.8') +#wxversion.select('2.9.5') # 2.9.x classic +#wxversion.select('3.0.1-msw-phoenix', optionsRequired=True) # 3.0 phoenix + + +import wx +print wx.VERSION_STRING import matplotlib matplotlib.interactive(False) matplotlib.use('WXAgg') @@ -11,7 +20,7 @@ class Knob: """ - Knob - simple class with a "setKnob" method. + Knob - simple class with a "setKnob" method. A Knob instance is attached to a Param instance, e.g., param.attach(knob) Base class is for documentation purposes. """ @@ -24,8 +33,8 @@ class Param: The idea of the "Param" class is that some parameter in the GUI may have several knobs that both control it and reflect the parameter's state, e.g. a slider, text, and dragging can all change the value of the frequency in - the waveform of this example. - The class allows a cleaner way to update/"feedback" to the other knobs when + the waveform of this example. + The class allows a cleaner way to update/"feedback" to the other knobs when one is being changed. Also, this class handles min/max constraints for all the knobs. Idea - knob list - in "set" method, knob object is passed as well @@ -39,10 +48,10 @@ def __init__(self, initialValue=None, minimum=0., maximum=1.): raise ValueError('illegal initial value') self.value = initialValue self.knobs = [] - + def attach(self, knob): self.knobs += [knob] - + def set(self, value, knob=None): self.value = value self.value = self.constrain(value) @@ -64,9 +73,10 @@ def __init__(self, parent, label, param): self.sliderLabel = wx.StaticText(parent, label=label) self.sliderText = wx.TextCtrl(parent, -1, style=wx.TE_PROCESS_ENTER) self.slider = wx.Slider(parent, -1) - self.slider.SetMax(param.maximum*1000) + #self.slider.SetMax(param.maximum*1000) + self.slider.SetRange(0, param.maximum*1000) self.setKnob(param.value) - + sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.sliderLabel, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=2) sizer.Add(self.sliderText, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=2) @@ -82,11 +92,11 @@ def __init__(self, parent, label, param): def sliderHandler(self, evt): value = evt.GetInt() / 1000. self.param.set(value) - + def sliderTextHandler(self, evt): value = float(self.sliderText.GetValue()) self.param.set(value) - + def setKnob(self, value): self.sliderText.SetValue('%g'%value) self.slider.SetValue(value*1000) @@ -109,7 +119,7 @@ def __init__(self, *args, **kwargs): sizer.Add(self.amplitudeSliderGroup.sizer, 0, \ wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=5) self.SetSizer(sizer) - + class FourierDemoWindow(wx.Window, Knob): def __init__(self, *args, **kwargs): @@ -125,7 +135,7 @@ def __init__(self, *args, **kwargs): self.f0 = Param(2., minimum=0., maximum=6.) self.A = Param(1., minimum=0.01, maximum=2.) self.draw() - + # Not sure I like having two params attached to the same Knob, # but that is what we have here... it works but feels kludgy - # although maybe it's not too bad since the knob changes both params @@ -133,10 +143,16 @@ def __init__(self, *args, **kwargs): self.f0.attach(self) self.A.attach(self) self.Bind(wx.EVT_SIZE, self.sizeHandler) - + + self.Bind(wx.EVT_PAINT, self.OnPaint) + + def OnPaint(self, event): + self.canvas.draw() + event.Skip() + def sizeHandler(self, *args, **kwargs): self.canvas.SetSize(self.GetSize()) - + def mouseDown(self, evt): if self.lines[0] in self.figure.hitlist(evt): self.state = 'frequency' @@ -159,7 +175,7 @@ def mouseMotion(self, evt): elif self.state == 'time': if (x-x0)/x0 != -1.: self.f0.set(1./(1./f0Init+(1./f0Init*(x-x0)/x0))) - + def mouseUp(self, evt): self.state = '' @@ -209,6 +225,6 @@ def OnInit(self): self.frame1 = FourierDemoFrame(parent=None, title="Fourier Demo", size=(640, 480)) self.frame1.Show() return True - + app = App() app.MainLoop() diff --git a/examples/user_interfaces/wxcursor_demo.py b/examples/user_interfaces/wxcursor_demo.py index 4d610ffc44d8..93be5f001364 100644 --- a/examples/user_interfaces/wxcursor_demo.py +++ b/examples/user_interfaces/wxcursor_demo.py @@ -1,6 +1,12 @@ """ Example to draw a cursor and report the data coords in wx """ +# Used to guarantee to use at least Wx2.8 +import wxversion +wxversion.ensureMinimal('2.8') +#wxversion.select('2.8') +#wxversion.select('2.9.5') # 2.9.x classic +#wxversion.select('2.9.6-msw-phoenix') # 2.9.x phoenix import matplotlib matplotlib.use('WXAgg') @@ -11,14 +17,17 @@ from numpy import arange, sin, pi import wx +print wx.VERSION_STRING class CanvasFrame(wx.Frame): def __init__(self, ): wx.Frame.__init__(self,None,-1, 'CanvasFrame',size=(550,350)) - - self.SetBackgroundColour(wx.NamedColour("WHITE")) + if 'phoenix' in wx.PlatformInfo: + self.SetBackgroundColour(wx.Colour("WHITE")) + else: + self.SetBackgroundColour(wx.NamedColour("WHITE")) self.figure = Figure() self.axes = self.figure.add_subplot(111) @@ -41,14 +50,23 @@ def __init__(self, ): self.statusBar = wx.StatusBar(self, -1) self.statusBar.SetFieldsCount(1) + self.statusBar.SetStatusWidths([-1]) self.SetStatusBar(self.statusBar) self.toolbar = NavigationToolbar2Wx(self.figure_canvas) self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) self.toolbar.Show() - + self.Bind(wx.EVT_PAINT, self.OnPaint) + + def OnPaint(self, event): + self.figure_canvas.draw() + event.Skip() + def ChangeCursor(self, event): - self.figure_canvas.SetCursor(wx.StockCursor(wx.CURSOR_BULLSEYE)) + if 'phoenix' in wx.PlatformInfo: + self.figure_canvas.SetCursor(wx.Cursor(wx.CURSOR_BULLSEYE)) + else: + self.figure_canvas.SetCursor(wx.StockCursor(wx.CURSOR_BULLSEYE)) def UpdateStatusBar(self, event): if event.inaxes: From 245a384120777690059bafb2d3c680f30c32ba42 Mon Sep 17 00:00:00 2001 From: Werner F Bruhin Date: Wed, 27 Aug 2014 17:19:36 +0200 Subject: [PATCH 87/90] - set a variable at import time for wxPython classic/phoenix version - only use wxversion in Py2 - remove BeginDrawing/EndDrawing --- lib/matplotlib/backends/backend_wx.py | 74 ++++++++++-------------- lib/matplotlib/backends/backend_wxagg.py | 13 +++-- 2 files changed, 38 insertions(+), 49 deletions(-) diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index 8a9043a3a18f..69f2b2803237 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -40,38 +40,36 @@ import traceback, pdb _DEBUG_lvls = {1 : 'Low ', 2 : 'Med ', 3 : 'High', 4 : 'Error' } -if six.PY3: - warnings.warn( - "The wx and wxagg backends have not been tested with Python 3.x", - ImportWarning) - -missingwx = "Matplotlib backend_wx and backend_wxagg require wxPython >=2.8" -missingwxversion = ("Matplotlib backend_wx and backend_wxagg " +if sys.version_info.major < 3: + # wxPython-Phoenix, does currently not support wxversion + missingwx = "Matplotlib backend_wx and backend_wxagg require wxPython >=2.8" + missingwxversion = ("Matplotlib backend_wx and backend_wxagg " "require wxversion, which was not found.") -if not hasattr(sys, 'frozen'): # i.e., not py2exe - try: - import wxversion - except ImportError: - raise ImportError(missingwxversion) - - # Some early versions of wxversion lack AlreadyImportedError. - # It was added around 2.8.4? - try: - _wx_ensure_failed = wxversion.AlreadyImportedError - except AttributeError: - _wx_ensure_failed = wxversion.VersionError - - try: - wxversion.ensureMinimal(str('2.8')) - except _wx_ensure_failed: - pass - # We don't really want to pass in case of VersionError, but when - # AlreadyImportedError is not available, we have to. + if not hasattr(sys, 'frozen'): # i.e., not py2exe + try: + import wxversion + except ImportError: + raise ImportError(missingwxversion) + + # Some early versions of wxversion lack AlreadyImportedError. + # It was added around 2.8.4? + try: + _wx_ensure_failed = wxversion.AlreadyImportedError + except AttributeError: + _wx_ensure_failed = wxversion.VersionError + + try: + wxversion.ensureMinimal(str('2.8')) + except _wx_ensure_failed: + pass + # We don't really want to pass in case of VersionError, but when + # AlreadyImportedError is not available, we have to. try: import wx backend_version = wx.VERSION_STRING + is_phoenix = 'phoenix' in wx.PlatformInfo except ImportError: raise ImportError(missingwx) @@ -732,7 +730,7 @@ def do_nothing(*args, **kwargs): # Create the drawing bitmap - if 'phoenix' in wx.PlatformInfo: + if is_phoenix: self.bitmap =wx.Bitmap(w, h) else: self.bitmap =wx.EmptyBitmap(w, h) @@ -872,7 +870,7 @@ def start_event_loop(self, timeout=0): bind(self, wx.EVT_TIMER, self.stop_event_loop, id=id) # Event loop handler for start/stop event loop - if 'phoenix' in wx.PlatformInfo: + if is_phoenix: self._event_loop = wx.GUIEventLoop() else: self._event_loop = wx.EventLoop() @@ -925,13 +923,7 @@ def gui_repaint(self, drawDC=None): if drawDC is None: drawDC=wx.ClientDC(self) - if 'phoenix' in wx.PlatformInfo: - drawDC.DrawBitmap(self.bitmap, 0, 0) - else: - drawDC.BeginDrawing() - drawDC.DrawBitmap(self.bitmap, 0, 0) - drawDC.EndDrawing() - #wx.GetApp().Yield() + drawDC.DrawBitmap(self.bitmap, 0, 0) else: pass @@ -984,7 +976,7 @@ def _print_image(self, filename, filetype, *args, **kwargs): width = int(math.ceil(width)) height = int(math.ceil(height)) - if 'phoenix' in wx.PlatformInfo: + if is_phoenix: self.bitmap =wx.Bitmap(width, height) else: self.bitmap =wx.EmptyBitmap(width, height) @@ -1061,7 +1053,7 @@ def _onSize(self, evt): DEBUG_MSG("_onSize()", 2, self) # Create a new, correctly sized bitmap self._width, self._height = self.GetClientSize() - if 'phoenix' in wx.PlatformInfo: + if is_phoenix: self.bitmap =wx.Bitmap(self._width, self._height) else: self.bitmap =wx.EmptyBitmap(self._width, self._height) @@ -1649,7 +1641,7 @@ def _init_toolbar(self): self.AddSeparator() continue self.wx_ids[text] = wx.NewId() - if 'phoenix' in wx.PlatformInfo: + if is_phoenix: if text in ['Pan', 'Zoom']: kind = wx.ITEM_CHECK else: @@ -1726,7 +1718,7 @@ def save_figure(self, *args): error_msg_wx(str(e)) def set_cursor(self, cursor): - if 'phoenix' in wx.PlatformInfo: + if is_phoenix: cursor = wx.Cursor(cursord[cursor]) else: cursor = wx.StockCursor(cursord[cursor]) @@ -1766,8 +1758,6 @@ def draw_rubberband(self, event, x0, y0, x1, y1): dc.ResetBoundingBox() - if not 'phoenix' in wx.PlatformInfo: - dc.BeginDrawing() height = self.canvas.figure.bbox.height y1 = height - y1 y0 = height - y0 @@ -1784,8 +1774,6 @@ def draw_rubberband(self, event, x0, y0, x1, y1): else: dc.DrawRectangle(*lastrect) #erase last self.lastrect = rect dc.DrawRectangle(*rect) - if not 'phoenix' in wx.PlatformInfo: - dc.EndDrawing() def set_status_bar(self, statbar): self.statbar = statbar diff --git a/lib/matplotlib/backends/backend_wxagg.py b/lib/matplotlib/backends/backend_wxagg.py index c3dbea202b54..6e9fe58093cb 100644 --- a/lib/matplotlib/backends/backend_wxagg.py +++ b/lib/matplotlib/backends/backend_wxagg.py @@ -12,6 +12,7 @@ FigureFrameWx, DEBUG_MSG, NavigationToolbar2Wx, error_msg_wx, \ draw_if_interactive, show, Toolbar, backend_version import wx +is_phoenix = 'phoenix' in wx.PlatformInfo class FigureFrameWxAgg(FigureFrameWx): @@ -136,7 +137,7 @@ def _convert_agg_to_wx_image(agg, bbox): """ if bbox is None: # agg => rgb -> image - if 'phoenix' in wx.PlatformInfo: + if is_phoenix: image = wx.Image(int(agg.width), int(agg.height)) else: image = wx.EmptyImage(int(agg.width), int(agg.height)) @@ -156,9 +157,9 @@ def _convert_agg_to_wx_bitmap(agg, bbox): """ if bbox is None: # agg => rgba buffer -> bitmap - if 'phoenix' in wx.PlatformInfo: + if is_phoenix: return wx.Bitmap.FromBufferRGBA(int(agg.width), int(agg.height), - agg.buffer_rgba()) + agg.buffer_rgba()) else: return wx.BitmapFromBufferRGBA(int(agg.width), int(agg.height), agg.buffer_rgba()) @@ -177,16 +178,16 @@ def _WX28_clipped_agg_as_bitmap(agg, bbox): r = l + width t = b + height - if 'phoenix' in wx.PlatformInfo: + if is_phoenix: srcBmp = wx.Bitmap.FromBufferRGBA(int(agg.width), int(agg.height), - agg.buffer_rgba()) + agg.buffer_rgba()) else: srcBmp = wx.BitmapFromBufferRGBA(int(agg.width), int(agg.height), agg.buffer_rgba()) srcDC = wx.MemoryDC() srcDC.SelectObject(srcBmp) - if 'phoenix' in wx.PlatformInfo: + if is_phoenix: destBmp = wx.Bitmap(int(width), int(height)) else: destBmp = wx.EmptyBitmap(int(width), int(height)) From cccfb2410cb0505c5d89b72d623eb816a7b4b558 Mon Sep 17 00:00:00 2001 From: Werner F Bruhin Date: Wed, 27 Aug 2014 17:21:12 +0200 Subject: [PATCH 88/90] - use wxversion only in Py2 - make it work in Py2.7 and Py3.4 with wxPython 2.8.12, 2.9.5 and 3.0.2Phoenix --- examples/user_interfaces/embedding_in_wx2.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/examples/user_interfaces/embedding_in_wx2.py b/examples/user_interfaces/embedding_in_wx2.py index 4d5e4ec1554b..6e2b90941d79 100644 --- a/examples/user_interfaces/embedding_in_wx2.py +++ b/examples/user_interfaces/embedding_in_wx2.py @@ -4,12 +4,14 @@ toolbar - comment out the setA_toolbar line for no toolbar """ -# Used to guarantee to use at least Wx2.8 -import wxversion -#wxversion.ensureMinimal('2.8') -wxversion.select('2.8') -#wxversion.select('2.9.5') # 2.9.x classic -#wxversion.select('3.0.1-msw-phoenix', optionsRequired=True) # 3.x Phoenix +import sys +if sys.version_info.major < 3: + # Used to guarantee to use at least Wx2.8 + import wxversion + wxversion.ensureMinimal('2.8') + #wxversion.select('2.8') + #wxversion.select('2.9.5') + #wxversion.select('3.0.2-phoenix', optionsRequired=True) from numpy import arange, sin, pi @@ -28,9 +30,9 @@ from matplotlib.figure import Figure import wx -print wx.VERSION_STRING -print wx.PlatformInfo -print matplotlib.__version__ +print(wx.VERSION_STRING) +print(wx.PlatformInfo) +print(matplotlib.__version__) class CanvasFrame(wx.Frame): From cdbde0bb248d8df09af608ebff66679d55afa3e6 Mon Sep 17 00:00:00 2001 From: Werner F Bruhin Date: Wed, 27 Aug 2014 17:33:43 +0200 Subject: [PATCH 89/90] - use wxversion only in Py2 - make it work on Py2.7 with wxPython 2.8.12, 2.9.5 and 3.0.2Phoenix - make it work on Py3.4 with 3.0.2Phoenix --- examples/user_interfaces/embedding_in_wx4.py | 15 ++++++++------- examples/user_interfaces/embedding_in_wx5.py | 17 ++++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/examples/user_interfaces/embedding_in_wx4.py b/examples/user_interfaces/embedding_in_wx4.py index f982ea8a1321..563c8c63cb31 100644 --- a/examples/user_interfaces/embedding_in_wx4.py +++ b/examples/user_interfaces/embedding_in_wx4.py @@ -4,13 +4,14 @@ toolbar """ -# Used to guarantee to use at least Wx2.8 -import wxversion -#wxversion.ensureMinimal('2.8') -wxversion.select('2.8') -#wxversion.select('2.9.5') # 2.9.x classic -#wxversion.select('3.0.1-msw-phoenix', optionsRequired=True) # 3.0 phoenix - +import sys +if sys.version_info.major < 3: + # Used to guarantee to use at least Wx2.8 + import wxversion + wxversion.ensureMinimal('2.8') + #wxversion.select('2.8') + #wxversion.select('2.9.5') + #wxversion.select('3.0.2-phoenix', optionsRequired=True) from numpy import arange, sin, pi diff --git a/examples/user_interfaces/embedding_in_wx5.py b/examples/user_interfaces/embedding_in_wx5.py index ec4f7ebff7e0..c57fb32089cb 100644 --- a/examples/user_interfaces/embedding_in_wx5.py +++ b/examples/user_interfaces/embedding_in_wx5.py @@ -1,13 +1,16 @@ -# Used to guarantee to use at least Wx2.8 -import wxversion -#wxversion.ensureMinimal('2.8') -wxversion.select('2.8') -#wxversion.select('2.9.5') # 2.9.x classic -#wxversion.select('3.0.1-msw-phoenix', optionsRequired=True) # 3.0 phoenix +#!/usr/bin/env python +import sys +if sys.version_info.major < 3: + # Used to guarantee to use at least Wx2.8 + import wxversion + wxversion.ensureMinimal('2.8') + #wxversion.select('2.8') + #wxversion.select('2.9.5') + #wxversion.select('3.0.2-phoenix', optionsRequired=True) import wx -print wx.VERSION_STRING +print(wx.VERSION_STRING) if 'phoenix' in wx.PlatformInfo: import wx.lib.agw.aui as aui From 11648f37e255e4cf5813b54cdee496a2930d44b5 Mon Sep 17 00:00:00 2001 From: Werner F Bruhin Date: Wed, 27 Aug 2014 17:35:18 +0200 Subject: [PATCH 90/90] - sometimes get an except: builtins.AttributeError: 'App' object has no attribute 'ProcessIdle' - use wxversion only in Py2 - make it work on Py2.7 with wxPython 2.8.12, 2.9.5 and 3.0.2Phoenix - make it work on Py3.4 with 3.0.2Phoenix --- examples/user_interfaces/fourier_demo_wx.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/examples/user_interfaces/fourier_demo_wx.py b/examples/user_interfaces/fourier_demo_wx.py index 1d2ee7971322..268a01be5526 100644 --- a/examples/user_interfaces/fourier_demo_wx.py +++ b/examples/user_interfaces/fourier_demo_wx.py @@ -1,15 +1,18 @@ +#!/usr/bin/env python import numpy as np -# Used to guarantee to use at least Wx2.8 -import wxversion -#wxversion.ensureMinimal('2.8') -wxversion.select('2.8') -#wxversion.select('2.9.5') # 2.9.x classic -#wxversion.select('3.0.1-msw-phoenix', optionsRequired=True) # 3.0 phoenix +import sys +if sys.version_info.major < 3: + # Used to guarantee to use at least Wx2.8 + import wxversion + wxversion.ensureMinimal('2.8') + #wxversion.select('2.8') + #wxversion.select('2.9.5') + #wxversion.select('3.0.2-phoenix', optionsRequired=True) import wx -print wx.VERSION_STRING +print(wx.VERSION_STRING) import matplotlib matplotlib.interactive(False) matplotlib.use('WXAgg')