diff --git a/ChangeLog b/ChangeLog index 5fed8e3..f568009 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +02-25-2016 Chris Barker + * Release 2.2.0 + * Assorted updated for wxPython >= 4.0 + * Other updated for modern MPL (tested on '2.1.2') + 02-25-2016 Chris Barker * re-structured to have simpler setup.py and stucture * added setuptools to allow develop mode. diff --git a/README.rst b/README.rst index bd8d283..c27846d 100644 --- a/README.rst +++ b/README.rst @@ -6,8 +6,8 @@ http://agni.phys.iit.edu/~kmcivor/wxmpl/ Ken has not maintained this for a while, and is no longer at IIT. -WxMpl - Painless matplolib embedding for wxPython -------------------------------------------------- +WxMpl - Painless matplotlib embedding for wxPython +-------------------------------------------------- The `wxmpl' module provides an matplotlib `FigureCanvas' with user-interaction features like point-under-cursor and zooming in on a selected area. @@ -24,51 +24,62 @@ subdirectory. REQUIREMENTS ------------ -* Python 2.5 or later +* Python 2.7 (would like to do a Py3 port, but not yet...) http://www.python.org -* wxPython 2.6.3.2 or later +* wxPython 4.0 or later http://www.wxpython.org -* matplotlib 0.98.1 or later +(It may work with earlier versions, but this is what it was tested on) + +* matplotlib 2.1 or later http://matplotlib.sourceforge.net +(Also may work for eariler versions, but >= 0.9.8 anyway) PLATFORMS --------- -WxMpl has been tested under Debian GNU/Linux 5.0 "Lenny" [wxPython 2.6.3.2] and Mac OS 10.5.6 [wxPython 2.8.9.1]. +WxMpl has been tested under Windows 10 and Mac OS 10.11 [wxPython 4.0.0]. INSTALLATION ------------ -The Python Distutils system provides packaging, compilation, and installation +The Python setuptools and pip system provides packaging, compilation, and installation for wxmpl. -To install, execute the following command as superuser: - # python setup.py install [OPTIONS] +To install, execute the following command as superuser:: + + $ python setup.py install [OPTIONS] + +Or: + + $ pip install ./ For more information about installation options, execute the following command: > python setup.py install --help -For information about other Distutils commands, execute the following command: +For information about other setuptools commands, execute the following command: > python setup.py install --help-commands AVAILABILITY ------------ -There is no website for WxMpl yet, so your best bet is to bug Ken. - -WxMpl's subversion repository is http://svn.csrri.iit.edu/mr-software/wxmpl/ +Project curently being manged on gitHub here: +https://github.com/NOAA-ORR-ERD/wxmpl AUTHOR ------ -WxMpl was written by Ken McIvor +WxMpl was written by: Ken McIvor + +Contirbutions from: Carlo Segre + +Currently maintained by: Chris Barker COPYRIGHT & LICENSE diff --git a/demos/picking_points.py b/demos/picking_points.py index c7470bd..ba5d585 100755 --- a/demos/picking_points.py +++ b/demos/picking_points.py @@ -18,7 +18,7 @@ class MyApp(wx.App): def OnInit(self): - self.frame = panel = MyFrame(None, -1, 'Point Picker') + self.frame = MyFrame(None, -1, 'Point Picker') self.frame.Show(True) return True @@ -31,12 +31,13 @@ def __init__(self, parent, id, title, **kwds): self.selectionPoints = [] self.plotPanel = wxmpl.PlotPanel(self, -1) self.regionButton = wx.ToggleButton(self, -1, 'Pick Region') - self.pointButton = wx.ToggleButton(self, -1, 'Pick Point') + self.pointButton = wx.ToggleButton(self, -1, 'Pick Point') - wx.EVT_TOGGLEBUTTON(self, self.regionButton.GetId(), - self._on_regionButton) + self.Bind(wx.EVT_TOGGLEBUTTON, self._on_regionButton, self.regionButton) + + # self.Bind(wxmpl.EVT_POINT, self._on_point, self.plotPanel) + wxmpl.EVT_POINT(self, self.plotPanel.GetId(), self._on_point) - wxmpl.EVT_POINT(self, self.plotPanel.GetId(), self._on_point) wxmpl.EVT_SELECTION(self, self.plotPanel.GetId(), self._on_selection) self._layout() @@ -46,12 +47,12 @@ def _layout(self): btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.Add((1, 1), 1, 0, 0) btnSizer.Add(self.regionButton, 0, wx.RIGHT, 5) - btnSizer.Add(self.pointButton, 0) + btnSizer.Add(self.pointButton, 0) btnSizer.Add((20, 1), 0, 0, 0) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.plotPanel, 1, wx.EXPAND, 5) - sizer.Add(btnSizer, 0, wx.ALIGN_RIGHT|wx.ALL, 5) + sizer.Add(btnSizer, 0, wx.ALIGN_RIGHT | wx.ALL, 5) self.SetSizer(sizer) self.Fit() @@ -116,10 +117,11 @@ def _on_selection(self, evt): x1, y1 = evt.x1data, evt.y1data x2, y2 = evt.x2data, evt.y2data - self.selectionPoints.append(((x1, y1), x2-x1, y2-y1)) + self.selectionPoints.append(((x1, y1), x2 - x1, y2 - y1)) self._replot() def _on_point(self, evt): + print "in _on_point", evt if self.pointButton.GetValue(): self.pointButton.SetValue(False) if evt.axes is not None: diff --git a/demos/plotting.py b/demos/plotting.py index 312314f..a0e1366 100755 --- a/demos/plotting.py +++ b/demos/plotting.py @@ -2,6 +2,9 @@ # Purpose: Demonstrates different plots from the matplotlib examples collection # Author: Ken McIvor , deriving from the matplotlib examples # collection + +# Updated for newer matplotlib by Chris Barker (PythonCHB@gmail.com) +# 2/1/2018 # # Copyright 2002-2004 John D. Hunter, 2005 Illinois Institute of Technology # @@ -12,21 +15,22 @@ # matplotlib 0.72 or http://matplotlib.sourceforge.net/license.html -__version__ = '1.0' +__version__ = '2.1' import wx import wxmpl import matplotlib import matplotlib.cm as cm -from pylab import array, arange, sin, cos, exp, pi, randn, normpdf, meshgrid, \ - convolve +import numpy as np + +from pylab import normpdf def plot_simple(fig): - t = arange(0.0, 2.0, 0.01) - s = sin(2*pi*t) - c = cos(2*pi*t) + t = np.arange(0.0, 2.0, 0.01) + s = np.sin(2 * np.pi * t) + c = np.cos(2 * np.pi * t) axes = fig.gca() axes.plot(t, s, linewidth=1.0) @@ -40,9 +44,9 @@ def plot_simple(fig): def plot_subplot(fig): def f(t): - return cos(2*pi*t) * exp(-t) - t1 = arange(0.0, 5.0, 0.10) - t2 = arange(0.0, 5.0, 0.02) + return np.cos(2 * np.pi * t) * np.exp(-t) + t1 = np.arange(0.0, 5.0, 0.10) + t2 = np.arange(0.0, 5.0, 0.02) a1 = fig.add_subplot(2, 1, 1) a1.plot(t1, f(t1), 'bo') @@ -52,7 +56,7 @@ def f(t): a1.set_ylabel('Damped oscillation') a2 = fig.add_subplot(2, 1, 2) - a2.plot(t2, cos(2*pi*t2), 'r>') + a2.plot(t2, np.cos(2 * np.pi * t2), 'r>') a2.grid(True) a2.set_xlabel('time (s)') a2.set_ylabel('Undamped') @@ -60,9 +64,9 @@ def f(t): def plot_subplot_sharex(fig): def f(t): - return cos(2*pi*t) * exp(-t) - t1 = arange(0.0, 5.0, 0.10) - t2 = arange(0.0, 5.0, 0.02) + return np.cos(2 * np.pi * t) * np.exp(-t) + t1 = np.arange(0.0, 5.0, 0.10) + t2 = np.arange(0.0, 5.0, 0.02) a1 = fig.add_subplot(2, 1, 1) a1.plot(t1, f(t1), 'bo') @@ -74,7 +78,7 @@ def f(t): ticklabel.set_visible(False) a2 = fig.add_subplot(2, 1, 2, sharex=a1) - a2.plot(t2, cos(2*pi*t2), 'r>') + a2.plot(t2, np.cos(2 * np.pi * t2), 'r>') a2.grid(True) a2.set_xlabel('time (s)') a2.set_ylabel('Undamped') @@ -82,14 +86,14 @@ def f(t): def plot_histogram(fig): mu, sigma = 100, 15 - x = mu + sigma*randn(10000) + x = mu + sigma * np.random.randn(10000) axes = fig.gca() # the histogram of the data n, bins, patches = axes.hist(x, 100, normed=1) # add a 'best fit' line - y = normpdf( bins, mu, sigma) + y = normpdf(bins, mu, sigma) l = axes.plot(bins, y, 'r--', linewidth=2) axes.set_xlim((40, 160)) @@ -100,25 +104,25 @@ def plot_histogram(fig): def plot_fill(fig): - t = arange(0.0, 1.01, 0.01) - s = sin(2*2*pi*t) + t = np.arange(0.0, 1.01, 0.01) + s = np.sin(2 * 2 * np.pi * t) axes = fig.gca() - axes.fill(t, s*exp(-5*t), 'r') + axes.fill(t, s * np.exp(-5 * t), 'r') axes.grid(True) def plot_log(fig): dt = 0.01 - t = arange(dt, 20.0, dt) + t = np.arange(dt, 20.0, dt) a1 = fig.add_subplot(2, 1, 1) - a1.semilogx(t, sin(2*pi*t)) + a1.semilogx(t, np.sin(2 * np.pi * t)) a1.set_ylabel('semilogx') a1.grid(True) a2 = fig.add_subplot(2, 1, 2) - a2.loglog(t, 20*exp(-t/10.0), basey=4) + a2.loglog(t, 20 * np.exp(-t / 10.0), basey=4) a2.xaxis.grid(True, which='minor') # minor grid on too a2.set_xlabel('time (s)') a2.set_ylabel('loglog') @@ -128,31 +132,32 @@ def plot_log(fig): def plot_polar(fig): import pylab - r = arange(0,1,0.001) - theta = 2*2*pi*r + r = np.arange(0, 1, 0.001) + theta = 2 * 2 * np.pi * r # radar green, solid grid lines matplotlib.rc('grid', color='#316931', linewidth=1, linestyle='-') - ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True, axisbg='#d5de9c') + ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True, facecolor='#d5de9c') ax.plot(theta, r, color='#ee8d18', lw=3) ax.set_title("And there was much rejoicing!", fontsize=14) matplotlib.rcdefaults() + def plot_polar_subplot(fig): # # Polar demo # import pylab - r = arange(0,1,0.001) - theta = 2*2*pi*r + r = np.arange(0, 1, 0.001) + theta = 2 * 2 * np.pi * r # radar green, solid grid lines matplotlib.rc('grid', color='#316931', linewidth=1, linestyle='-') - ax = fig.add_subplot(1, 2, 1, polar=True, axisbg='#d5de9c') + ax = fig.add_subplot(1, 2, 1, polar=True, facecolor='#d5de9c') ax.plot(theta, r, color='#ee8d18', lw=3) ax.set_title("And there was much rejoicing!", fontsize=14) @@ -162,10 +167,10 @@ def plot_polar_subplot(fig): # First part of the subplot demo # def f(t): - return cos(2*pi*t) * exp(-t) + return np.cos(2 * np.pi * t) * np.exp(-t) - t1 = arange(0.0, 5.0, 0.10) - t2 = arange(0.0, 5.0, 0.02) + t1 = np.arange(0.0, 5.0, 0.10) + t2 = np.arange(0.0, 5.0, 0.02) A1 = fig.add_subplot(1, 2, 2) A1.plot(t1, f(t1), 'bo') @@ -178,18 +183,15 @@ def f(t): def plot_legend(fig): - a = arange(0,3,.02) - b = arange(0,3,.02) - c = exp(a) - d = c.tolist() - d.reverse() - d = array(d) + a = np.arange(0, 3, .02) + c = np.exp(a) + d = np.fromiter(reversed(c), dtype=c.dtype) axes = fig.gca() - axes.plot(a,c,'k--',a,d,'k:',a,c+d,'k') - axes.legend(('Model length', 'Data length', 'Total message length'), - 'upper center', shadow=True) - axes.set_ylim([-1,20]) + lines = axes.plot(a, c, 'k--', a, d, 'k:', a, c + d, 'k') + names = ('Model length', 'Data length', 'Total message length') + axes.legend(lines, names, loc='upper center', shadow=True) + axes.set_ylim([-1, 20]) axes.grid(False) axes.set_xlabel('Model complexity --->') axes.set_ylabel('Message length --->') @@ -199,29 +201,29 @@ def plot_legend(fig): def plot_image(fig): - def func3(x,y): - return (1- x/2 + x**5 + y**3)*exp(-x**2-y**2) + def func3(x, y): + return (1 - x / 2 + x**5 + y**3) * np.exp(-x**2 - y**2) dx, dy = 0.025, 0.025 - x = arange(-3.0, 3.0, dx) - y = arange(-3.0, 3.0, dy) - X,Y = meshgrid(x, y) + x = np.arange(-3.0, 3.0, dx) + y = np.arange(-3.0, 3.0, dy) + X, Y = np.meshgrid(x, y) Z = func3(X, Y) axes = fig.gca() - im = axes.imshow(Z, cmap=cm.jet, extent=(-3, 3, -3, 3)) + axes.imshow(Z, cmap=cm.jet, extent=(-3, 3, -3, 3)) def plot_layered_images(fig): - def func3(x,y): - return (1- x/2 + x**5 + y**3)*exp(-x**2-y**2) + def func3(x, y): + return (1 - x / 2 + x**5 + y**3) * np.exp(-x**2 - y**2) # make these smaller to increase the resolution dx, dy = 0.05, 0.05 - x = arange(-3.0, 3.0, dx) - y = arange(-3.0, 3.0, dy) - X,Y = meshgrid(x, y) + x = np.arange(-3.0, 3.0, dx) + y = np.arange(-3.0, 3.0, dy) + X, Y = np.meshgrid(x, y) # when layering multiple images, the images need to have the same # extent. This does not mean they need to have the same shape, but @@ -230,43 +232,42 @@ def func3(x,y): xmin, xmax, ymin, ymax = min(x), max(x), min(y), max(y) extent = xmin, xmax, ymin, ymax - Z1 = array(([0,1]*4 + [1,0]*4)*4); Z1.shape = 8,8 # chessboard + Z1 = np.array(([0, 1] * 4 + [1, 0] * 4) * 4) + Z1.shape = 8, 8 # chessboard Z2 = func3(X, Y) axes = fig.gca() - axes.imshow(Z1, cmap=cm.gray, interpolation='nearest', - extent=extent) - axes.hold(True) - axes.imshow(Z2, cmap=cm.jet, alpha=.9, interpolation='bilinear', - extent=extent) + axes.imshow(Z1, cmap=cm.gray, interpolation='nearest', extent=extent) + axes.imshow(Z2, cmap=cm.jet, alpha=.9, + interpolation='bilinear', extent=extent) def plot_axes(fig): # create some data to use for the plot dt = 0.001 - t = arange(0.0, 10.0, dt) - r = exp(-t[:1000]/0.05) # impulse response - x = randn(len(t)) - s = convolve(x,r,mode=2)[:len(x)]*dt # colored noise + t = np.arange(0.0, 10.0, dt) + r = np.exp(-t[:1000] / 0.05) # impulse response + x = np.random.randn(len(t)) + s = np.convolve(x, r, mode=2)[:len(x)] * dt # colored noise # the main axes is subplot(111) by default axes = fig.gca() axes.plot(t, s) axes.set_xlim((0, 1)) - axes.set_ylim((1.1*min(s), 2*max(s))) + axes.set_ylim((1.1 * min(s), 2 * max(s))) axes.set_xlabel('time (s)') axes.set_ylabel('current (nA)') axes.set_title('Gaussian colored noise') # this is an inset axes over the main axes - a = fig.add_axes([.65, .6, .2, .2], axisbg='y') + a = fig.add_axes([.65, .6, .2, .2], facecolor='y') n, bins, patches = a.hist(s, 400, normed=1) a.set_title('Probability') a.set_xticks([]) a.set_yticks([]) # this is another inset axes over the main axes - a = fig.add_axes([.2, .6, .2, .2], axisbg='y') + a = fig.add_axes([.2, .6, .2, .2], facecolor='y') a.plot(t[:len(r)], r) a.set_title('Impulse response') a.set_xlim((0, 0.2)) @@ -279,6 +280,7 @@ def plot_axes(fig): # class Demo: + def __init__(self, title, plotFunction, size=(6.0, 3.7), dpi=96): self.title = title self.plotFunction = plotFunction @@ -286,15 +288,18 @@ def __init__(self, title, plotFunction, size=(6.0, 3.7), dpi=96): self.dpi = dpi def run(self): - frame = wxmpl.PlotFrame(None, -1, self.title, size=self.size, - dpi=self.dpi) + frame = wxmpl.PlotFrame(None, + -1, + self.title, + size=self.size, + dpi=self.dpi) self.plotFunction(frame.get_figure()) frame.draw() frame.Show() def makeButton(self, parent): btn = wx.Button(parent, -1, self.title) - wx.EVT_BUTTON(btn, btn.GetId(), self.OnButton) + btn.Bind(wx.EVT_BUTTON, self.OnButton) return btn def OnButton(self, evt): @@ -309,7 +314,7 @@ def OnButton(self, evt): Demo('Filled Polygons', plot_fill), Demo('Logarithmic Scaling', plot_log), Demo('Polar Plot', plot_polar, (6.0, 6.0)), - Demo('Polar and Linear Subplots', plot_polar_subplot, (8.0,4.0)), + Demo('Polar and Linear Subplots', plot_polar_subplot, (8.0, 4.0)), Demo('Linear Plot with a Legend', plot_legend), Demo('Pseudocolor Image', plot_image), Demo('Layered Images', plot_layered_images), @@ -318,27 +323,28 @@ def OnButton(self, evt): class TestFrame(wx.Frame): - def __init__(self, parent, id, title, **kwds): - wx.Frame.__init__(self, parent, id, title, **kwds) + + def __init__(self, *args, **kwargs): + wx.Frame.__init__(self, *args, **kwargs) buttons = [demo.makeButton(self) for demo in DEMONSTRATIONS] sizer = wx.BoxSizer(wx.VERTICAL) for btn in buttons: - sizer.Add(btn, 0, wx.EXPAND|wx.ALL, 5) + sizer.Add(btn, 0, wx.EXPAND | wx.ALL, 5) self.SetSizer(sizer) self.Fit() - wx.EVT_WINDOW_DESTROY(self, self.OnWindowDestroy) + self.Bind(wx.EVT_WINDOW_DESTROY, self.OnWindowDestroy) def OnWindowDestroy(self, evt): wx.GetApp().ExitMainLoop() def main(): - app = wx.PySimpleApp() - frame = TestFrame(None, -1, 'WxMpl Demos') + app = wx.App() + frame = TestFrame(None, title='WxMpl Demos') frame.Show(True) app.MainLoop() diff --git a/wxmpl.py b/wxmpl.py index fd51101..00495b2 100644 --- a/wxmpl.py +++ b/wxmpl.py @@ -15,45 +15,35 @@ import wx -import sys +import wx.lib.newevent import os.path import weakref import matplotlib -matplotlib.use('WXAgg') -import numpy as NumPy +matplotlib.use('WXAgg') # noqa +import numpy as np from matplotlib.axes._base import _process_plot_var_args -from matplotlib.backend_bases import FigureCanvasBase -from matplotlib.backends.backend_agg import FigureCanvasAgg, RendererAgg +# from matplotlib.backend_bases import FigureCanvasBase +from matplotlib.backends.backend_agg import RendererAgg +# from matplotlib.backends.backend_agg import FigureCanvasAgg, RendererAgg from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg from matplotlib.figure import Figure from matplotlib.font_manager import FontProperties -from matplotlib.projections.polar import PolarAxes from matplotlib.transforms import Bbox -__version__ = '2.1.0dev1' +__version__ = '2.2.0' __all__ = ['PlotPanel', 'PlotFrame', 'PlotApp', 'StripCharter', 'Channel', - 'FigurePrinter', 'PointEvent', 'EVT_POINT', 'SelectionEvent', - 'EVT_SELECTION'] + 'FigurePrinter', 'PointEvent', 'EVT_POINT', 'SelectionEvent', + 'EVT_SELECTION'] # If you are using wxGtk without libgnomeprint and want to use something other # than `lpr' to print you will have to specify that command here. POSTSCRIPT_PRINTING_COMMAND = 'lpr' -# Between 0.98.1 and 0.98.3rc there were some significant API changes: -# * FigureCanvasWx.draw(repaint=True) became draw(drawDC=None) -# * The following events were added: -# - figure_enter_event -# - figure_leave_event -# - axes_enter_event -# - axes_leave_event -MATPLOTLIB_0_98_3 = '0.98.3' <= matplotlib.__version__ - - # # Utility functions and classes -# + def invert_point(x, y, transform): """ @@ -152,10 +142,11 @@ def toplevel_parent_of_window(window): """ Returns the first top-level parent of a wx.Window """ - topwin = window - while not isinstance(topwin, wx.TopLevelWindow): - topwin = topwin.GetParent() - return topwin + topwin = wx.GetTopLevelParent(window) + if topwin is None: + return window + else: + return topwin class AxesLimits: @@ -525,7 +516,6 @@ def _paint(self, value, dc): dc.SetTextForeground(self.TEXT_FOREGROUND) dc.SetTextBackground(self.TEXT_BACKGROUND) dc.SetLogicalFunction(self.FUNCTION) - dc.BeginDrawing() if self.lastValue is not None: self.clearValue(dc, self.lastValue) @@ -535,8 +525,6 @@ def _paint(self, value, dc): self.drawValue(dc, value) self.lastValue = value - dc.EndDrawing() - def formatValue(self, value): """ Template method that processes the C{value} tuple passed to the @@ -545,6 +533,7 @@ def formatValue(self, value): return value def drawValue(self, dc, value): + # fixme -- BeginDrawing and EndDrawing no longer exist -- this still needed? """ Template method that draws a previously processed C{value} using the wxPython device context C{dc}. This DC has already been configured, so @@ -553,6 +542,7 @@ def drawValue(self, dc, value): pass def clearValue(self, dc, value): + # fixme -- BeginDrawing and EndDrawing no longer exist -- this still needed? """ Template method that clears a previously processed C{value} that was previously drawn, using the wxPython device context C{dc}. This DC has @@ -752,7 +742,7 @@ def update_postscript_resolution(printData): dpi = PS_DPI_DRAFT_QUALITY else: dpi = PS_DPI_HIGH_QUALITY - + wx.PostScriptDC_SetResolution(dpi) @@ -992,20 +982,37 @@ def render_figure_as_image(self, wFig, hFig, dpi): image.SetData(agg.tostring_rgb()) return image +# wxPython event emitted when a left-click-release occurs in a matplotlib +# axes of a window without an area selection. +# +# When created, it should be called with the following keyword arguments: +# axes: matplotlib Axes which was left-clicked +# x: matplotlib X coordinate +# y: matplotlib Y coordinate +# @cvar xdata: axes X coordinate +# @cvar ydata: axes Y coordinate + +# The "new", easy way -- but I keep getting and error trying to use it :-() +# PointEvent, EVT_POINT = wx.lib.newevent.NewEvent() +# PointEvent, EVT_POINT = wx.lib.newevent.NewCommandEvent() # # wxPython event interface for the PlotPanel and PlotFrame # -EVT_POINT_ID = wx.NewId() +# EVT_POINT_ID = wx.NewId() -def EVT_POINT(win, id, func): - """ - Register to receive wxPython C{PointEvent}s from a C{PlotPanel} or - C{PlotFrame}. - """ - win.Connect(id, -1, EVT_POINT_ID, func) +# # def EVT_POINT(win, id, func): +# # # fixme: Is this the right way to do it -- can we Bind() instead??? +# # """ +# # Register to receive wxPython C{PointEvent}s from a C{PlotPanel} or +# # C{PlotFrame}. +# # """ +# # win.Connect(id, -1, EVT_POINT_ID, func) + + +EVT_POINT_ID = wx.NewId() class PointEvent(wx.PyCommandEvent): @@ -1034,6 +1041,16 @@ def Clone(self): return PointEvent(self.GetId(), self.axes, self.x, self.y) +# EVT_POINT = wx.PyEventBinder(PointEvent) + +def EVT_POINT(win, id, func): + """ + Register to receive wxPython C{SelectionEvent}s from a C{PlotPanel} or + C{PlotFrame}. + """ + win.Connect(id, -1, EVT_POINT_ID, func) + + EVT_SELECTION_ID = wx.NewId() @@ -1089,9 +1106,17 @@ class PlotPanel(FigureCanvasWxAgg): """ A matplotlib canvas suitable for embedding in wxPython applications. """ - def __init__(self, parent, id, size=(6.0, 3.70), dpi=96, cursor=True, - location=True, crosshairs=True, selection=True, zoom=True, - autoscaleUnzoom=True): + def __init__(self, + parent, + id=wx.ID_ANY, + size=(6.0, 3.70), + dpi=96, + cursor=True, + location=True, + crosshairs=True, + selection=True, + zoom=True, + autoscaleUnzoom=True): """ Creates a new PlotPanel window that is the child of the wxPython window C{parent} with the wxPython identifier C{id}. @@ -1113,9 +1138,12 @@ def __init__(self, parent, id, size=(6.0, 3.70), dpi=96, cursor=True, self.location = LocationPainter(self, location) self.crosshairs = CrosshairPainter(self, crosshairs) self.rubberband = RubberbandPainter(self, selection) - rightClickUnzoom = True # for now this is default behavior - self.director = PlotPanelDirector(self, zoom, selection, - rightClickUnzoom, autoscaleUnzoom) + rightClickUnzoom = True # for now this is default behavior + self.director = PlotPanelDirector(self, + zoom, + selection, + rightClickUnzoom, + autoscaleUnzoom) self.figure.set_edgecolor('black') self.figure.set_facecolor('white') @@ -1124,10 +1152,11 @@ def __init__(self, parent, id, size=(6.0, 3.70), dpi=96, cursor=True, # find the toplevel parent window and register an activation event # handler that is keyed to the id of this PlotPanel topwin = toplevel_parent_of_window(self) - topwin.Connect(-1, self.GetId(), wx.wxEVT_ACTIVATE, self.OnActivate) + topwin.Bind(wx.EVT_ACTIVATE, self.OnActivate) + # topwin.Connect(wx.ID_ANY, self.GetId(), wx.wxEVT_ACTIVATE, self.OnActivate) - wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground) - wx.EVT_WINDOW_DESTROY(self, self.OnDestroy) + self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) + self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) def OnActivate(self, evt): """ @@ -1150,7 +1179,8 @@ def OnDestroy(self, evt): """ Handles the wxPython window destruction event. """ - if self.GetId() == evt.GetEventObject().GetId(): + # fixme: is this necessary at all? + if self.Id == evt.EventObject.Id: # unregister the activation event handler for this PlotPanel topwin = toplevel_parent_of_window(self) topwin.Disconnect(-1, self.GetId(), wx.wxEVT_ACTIVATE) @@ -1229,20 +1259,16 @@ def zoomed(self, axes): """ return self.director.zoomed(axes) - def draw(self, **kwds): + def draw(self, **kwargs): """ Draw the associated C{Figure} onto the screen. """ # don't redraw if the left mouse button is down and avoid # wxPyDeadObject errors - if (not self.director.canDraw() - or not isinstance(self, FigureCanvasWxAgg)): + if (not self.director.canDraw() or not isinstance(self, FigureCanvasWxAgg)): return - if MATPLOTLIB_0_98_3: - FigureCanvasWxAgg.draw(self, kwds.get('drawDC', None)) - else: - FigureCanvasWxAgg.draw(self, kwds.get('repaint', True)) + FigureCanvasWxAgg.draw(self, kwargs.get('drawDC', None)) # Don't redraw the decorations when called by _onPaint() if not self.insideOnPaint: @@ -1254,7 +1280,9 @@ def notify_point(self, axes, x, y): """ Called by the associated C{PlotPanelDirector} to emit a C{PointEvent}. """ - wx.PostEvent(self, PointEvent(self.GetId(), axes, x, y)) + # xdata, ydata = invert_point(x, y, axes.transData) + evt = PointEvent(self.GetId(), axes, x, y) + wx.PostEvent(self, evt) def notify_selection(self, axes, x1, y1, x2, y2): """ @@ -1283,7 +1311,7 @@ def _onKeyUp(self, evt): dispatching the event to the associated C{PlotPanelDirector}. """ self.director.keyUp(evt) - + def _onLeftButtonDown(self, evt): """ Overrides the C{FigureCanvasWxAgg} left-click event handler, @@ -1338,13 +1366,23 @@ class PlotFrame(wx.Frame): """ ABOUT_TITLE = 'About wxmpl.PlotFrame' - ABOUT_MESSAGE = ('wxmpl.PlotFrame %s\n' % __version__ - + 'Written by Ken McIvor \n' - + 'Copyright 2005-2009 Illinois Institute of Technology') - - def __init__(self, parent, id, title, size=(6.0, 3.7), dpi=96, cursor=True, - location=True, crosshairs=True, selection=True, zoom=True, - autoscaleUnzoom=True, **kwds): + ABOUT_MESSAGE = ('wxmpl.PlotFrame %s\n' + 'Written by Ken McIvor \n' + 'Copyright 2005-2009 Illinois Institute of Technology' % __version__) + + def __init__(self, + parent, + id=wx.ID_ANY, + title="wxmpl.PlotFrame", + size=(6.0, 3.7), + dpi=96, + cursor=True, + location=True, + crosshairs=True, + selection=True, + zoom=True, + autoscaleUnzoom=True, + **kwargs): """ Creates a new PlotFrame top-level window that is the child of the wxPython window C{parent} with the wxPython identifier C{id} and the @@ -1356,9 +1394,17 @@ def __init__(self, parent, id, title, size=(6.0, 3.7), dpi=96, cursor=True, Any additional keyword arguments are passed to the constructor of C{wx.Frame}. """ - wx.Frame.__init__(self, parent, id, title, **kwds) - self.panel = PlotPanel(self, -1, size, dpi, cursor, location, - crosshairs, selection, zoom) + wx.Frame.__init__(self, parent, id, title, **kwargs) + + self.panel = PlotPanel(self, + wx.ID_ANY, + size, + dpi, + cursor, + location, + crosshairs, + selection, + zoom) pData = wx.PrintData() pData.SetPaperId(wx.PAPER_LETTER) @@ -1368,7 +1414,7 @@ def __init__(self, parent, id, title, size=(6.0, 3.7), dpi=96, cursor=True, self.create_menus() sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(self.panel, 1, wx.ALL|wx.EXPAND, 5) + sizer.Add(self.panel, 1, wx.ALL | wx.EXPAND, 5) self.SetSizer(sizer) self.Fit() @@ -1376,41 +1422,38 @@ def create_menus(self): mainMenu = wx.MenuBar() menu = wx.Menu() - id = wx.NewId() - menu.Append(id, '&Save As...\tCtrl+S', - 'Save a copy of the current plot') - wx.EVT_MENU(self, id, self.OnMenuFileSave) + menu_item = menu.Append(wx.ID_ANY, + '&Save As...\tCtrl+S', + 'Save a copy of the current plot') + self.Bind(wx.EVT_MENU, self.OnMenuFileSave, menu_item) menu.AppendSeparator() if wx.Platform != '__WXMAC__': - id = wx.NewId() - menu.Append(id, 'Page Set&up...', - 'Set the size and margins of the printed figure') - wx.EVT_MENU(self, id, self.OnMenuFilePageSetup) + menu_item = menu.Append(wx.ID_ANY, + 'Page Set&up...', + 'Set the size and margins of the printed figure') + self.Bind(wx.EVT_MENU, self.OnMenuFilePageSetup, menu_item) - id = wx.NewId() - menu.Append(id, 'Print Pre&view...', - 'Preview the print version of the current plot') - wx.EVT_MENU(self, id, self.OnMenuFilePrintPreview) + menu_item = menu.Append(wx.ID_ANY, + 'Print Pre&view...', + 'Preview the print version of the current plot') + self.Bind(wx.EVT_MENU, self.OnMenuFilePrintPreview, menu_item) - id = wx.NewId() - menu.Append(id, '&Print...\tCtrl+P', 'Print the current plot') - wx.EVT_MENU(self, id, self.OnMenuFilePrint) + menu_item = menu.Append(wx.ID_ANY, '&Print...\tCtrl+P', 'Print the current plot') + self.Bind(wx.EVT_MENU, self.OnMenuFilePrint, menu_item) menu.AppendSeparator() - id = wx.NewId() - menu.Append(id, '&Close Window\tCtrl+W', - 'Close the current plot window') - wx.EVT_MENU(self, id, self.OnMenuFileClose) + menu_item = menu.Append(wx.ID_ANY, '&Close Window\tCtrl+W', + 'Close the current plot window') + self.Bind(wx.EVT_MENU, self.OnMenuFileClose, menu_item) mainMenu.Append(menu, '&File') menu = wx.Menu() - id = wx.NewId() - menu.Append(id, '&About...', 'Display version information') - wx.EVT_MENU(self, id, self.OnMenuHelpAbout) + menu_item = menu.Append(wx.ID_ANY, '&About...', 'Display version information') + self.Bind(wx.EVT_MENU, self.OnMenuHelpAbout, menu_item) mainMenu.Append(menu, '&Help') self.SetMenuBar(mainMenu) @@ -1553,7 +1596,7 @@ class PlotApp(wx.App): ABOUT_MESSAGE = None def __init__(self, title="WxMpl", size=(6.0, 3.7), dpi=96, cursor=True, - location=True, crosshairs=True, selection=True, zoom=True, **kwds): + location=True, crosshairs=True, selection=True, zoom=True, **kwargs): """ Creates a new PlotApp, which creates a PlotFrame top-level window. @@ -1574,7 +1617,7 @@ def __init__(self, title="WxMpl", size=(6.0, 3.7), dpi=96, cursor=True, self.crosshairs = crosshairs self.selection = selection self.zoom = zoom - wx.App.__init__(self, **kwds) + wx.App.__init__(self, **kwargs) def OnInit(self): self.frame = panel = PlotFrame(None, -1, self.title, self.size, @@ -1649,7 +1692,7 @@ class VectorBuffer: accomodate new entries. """ def __init__(self): - self.data = NumPy.zeros((16,), dtype=float) + self.data = np.zeros((16,), dtype=float) self.nextRow = 0 def clear(self): @@ -1663,7 +1706,7 @@ def reset(self): """ Zero and reset this buffer, releasing the underlying array. """ - self.data = NumPy.zeros((16,), dtype=float) + self.data = np.zeros((16,), dtype=float) self.nextRow = 0 def append(self, point): @@ -1675,11 +1718,11 @@ def append(self, point): resize = False if nextRow == data.shape[0]: - nR = int(NumPy.ceil(self.data.shape[0]*1.5)) + nR = int(np.ceil(self.data.shape[0]*1.5)) resize = True if resize: - self.data = NumPy.zeros((nR,), dtype=float) + self.data = np.zeros((nR,), dtype=float) self.data[0:data.shape[0]] = data self.data[nextRow] = point @@ -1701,7 +1744,7 @@ class MatrixBuffer: accomodate new rows of entries. """ def __init__(self): - self.data = NumPy.zeros((16, 1), dtype=float) + self.data = np.zeros((16, 1), dtype=float) self.nextRow = 0 def clear(self): @@ -1715,14 +1758,14 @@ def reset(self): """ Zero and reset this buffer, releasing the underlying array. """ - self.data = NumPy.zeros((16, 1), dtype=float) + self.data = np.zeros((16, 1), dtype=float) self.nextRow = 0 def append(self, row): """ Append a new row of entries to the end of this buffer's matrix. """ - row = NumPy.asarray(row, dtype=float) + row = np.asarray(row, dtype=float) nextRow = self.nextRow data = self.data nPts = row.shape[0] @@ -1733,7 +1776,7 @@ def append(self, row): resize = True if nextRow == data.shape[0]: nC = data.shape[1] - nR = int(NumPy.ceil(self.data.shape[0]*1.5)) + nR = int(np.ceil(self.data.shape[0]*1.5)) if nC < nPts: nC = nPts elif data.shape[1] < nPts: @@ -1743,7 +1786,7 @@ def append(self, row): resize = False if resize: - self.data = NumPy.zeros((nR, nC), dtype=float) + self.data = np.zeros((nR, nC), dtype=float) rowEnd, colEnd = data.shape self.data[0:rowEnd, 0:colEnd] = data @@ -1935,9 +1978,9 @@ def _update_channel(self, channel, zoomed): xys = axes._get_verts_in_data_coords( line.get_transform(), zip(x, y)) else: - xys = NumPy.zeros((x.shape[0], 2), dtype=float) - xys[:,0] = x - xys[:,1] = y + xys = np.zeros((x.shape[0], 2), dtype=float) + xys[:, 0] = x + xys[:, 1] = y axes.update_datalim(xys) if zoomed: