From: Bogdan M. <bogdanal@b.astral.ro> - 2004-07-21 18:17:13
|
Hello all, First I want to apologize for sending this message to both lists, but I couldn't decide which one is more appropriate and I would like to have an answer as soon as possible. I discovered matplotlib some while ago, but it wasn't until recently when I decided to integrate it into one of my programs, trying to replace PyPlot that doesn't have enough features for me. I downloaded and installed version 0.60.2. Your library seemed perfect, until I discovered some strange problems that prevented me from using it as this point :( Specifically, I'm reffering to the embedding_in_wx2.py example that I found in the 'examples' directory. I modified it as follows (sorry for inlining, but I don't know if the listserver accepts any attachements at all, I didn't use it before): ================================================================= #!/usr/bin/env python """ An example of how to use wx or wxagg in an application w. or w/o the toolbar """ from matplotlib.numerix import arange, sin, pi import matplotlib # uncomment the following to use wx rather than wxagg #matplotlib.use('WX') #from matplotlib.backends.backend_wx import FigureCanvasWx as FigureCanvas # comment out the following to use wx rather than wxagg matplotlib.use('WXAgg') from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas from matplotlib.figure import Figure from wxPython.wx import * class Cursor: def __init__(self, canvas, ax): self.canvas = canvas self.ax = ax def mouse_move(self, widget, event): height = self.ax.figure.bbox.height() x, y = event.x, height-event.y if self.ax.in_axes(x, y): # transData transforms data coords to display coords. Use # the inverse method to transform back to data coords then # update the line # the cursor position x, y = self.ax.transData.inverse_xy_tup( (x,y) ) # the view limits minx, maxx = self.ax.viewLim.intervalx().get_bounds() miny, maxy = self.ax.viewLim.intervaly().get_bounds() print 'x=%1.2f, y=%1.2f'%(x,y) class CanvasFrame(wxFrame): def __init__(self): wxFrame.__init__(self,None,-1, 'CanvasFrame',size=( 900,900 ) ) self.SetBackgroundColour(wxNamedColor("WHITE")) self.figure = Figure( ) self.axes = self.figure.add_subplot(111) t = arange(0.0,3.0,0.01) s = sin(2*pi*t) c = sin(4*pi*t) p = self.axes.fill(t,s,'b',t,c,'g') p[ 0 ].set_alpha( 0.2 ) p[ 1 ].set_alpha( 0.2 ) #p = self.axes.fill(t,s,'b') #p[ 0 ].set_alpha( 0.2 ) #p[ 1 ].set_alpha( 0.2 ) #self.axes.plot(t,c,'g') self.axes.vlines( [1.5], -1.0, 1.0 ) self.canvas = FigureCanvas(self, -1, self.figure) self.sizer = wxBoxSizer(wxVERTICAL) self.sizer.Add(self.canvas, 1, wxTOP | wxLEFT | wxEXPAND) self.SetSizer(self.sizer) self.SetAutoLayout( True ) # self.Fit() self.sizer.Fit( self ) cursor = Cursor(self.canvas, self.axes) #cursor = SnaptoCursor(canvas, ax, t, s) self.canvas.connect('motion_notify_event', cursor.mouse_move) #self.add_toolbar() # comment this out for no toolbar # Capture the paint message EVT_PAINT(self, self.OnPaint) EVT_SIZE( self, self.OnSize) def add_toolbar(self): self.toolbar = NavigationToolbar(self.canvas, True) self.toolbar.Realize() if wxPlatform == '__WXMAC__': # Mac platform (OSX 10.3, MacPython) does not seem to cope with # having a toolbar in a sizer. This work-around gets the buttons # back, but at the expense of having the toolbar at the top self.SetToolBar(self.toolbar) 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() # 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. self.toolbar.SetSize(wxSize(fw, th)) self.sizer.Add(self.toolbar, 0, wxLEFT | wxEXPAND) # update the axes menu on the toolbar self.toolbar.update() def OnPaint(self, event): # self.canvas.draw() print "OnPaint" # if type( event ) == type( wxEvent ): event.Skip() def OnSize( self, event ): print "OnSize" print event.GetSize() event.Skip() # event.Skip() class App(wxApp): def OnInit(self): 'Create the main window and insert the custom frame' frame = CanvasFrame() frame.Show(true) return true app = App(0) app.MainLoop() ================================================================= The example shows just fine at first, but when I try to resize the windows it crashes (note that the original example also crashed under the same conditions). By "crashes" I mean that it exits with an unhandled exception, and Windows shows that the error is in _backend_agg.pyd. It also crashes if I change the initial figure size in .matplotlibrc (when changing figure.figsize from 8, 6 to 10, 10). I tried running in with Python 2.3.2 and Python 2.3.4, with wxPython 2.4.2.4 and wxPython 2.5.1.5, but the result was the same. I inserted some debug statements in backend_wxagg.py and it seems that the crash is in 's = agg.tostring_rgb()', the program doesn't reach the line after this call. I modified the above code in a number of ways (including changing the code for OnPaint and OnSize as the documentation is not clear at all about their role and I don't know what I should do in these handlers), but I was unable to solve the problem. Note that the same example works fine with the simple WX backend, but this is no option for me, as I would really need alpha blending, and antialiasing wouldn't hurt too. Also, I need help with the initial plot size. When I create the plot it has very large margins to the parent frame. This is not convenient to me, as I'd like to have a plot that is as large as possible and margins as small as possible. Is there a way to modify these margins? Thank you for your patience in reading this e-mail and please help me if you can. I'm not subscribed to either matplotlib-users or matplotlib-devel, so please send your replies with a copy to bogdanal@b.astral.ro, or at least let me know if I need to be subscribed to the list in order to receive your answers regarding this issue. Thank you very much for your help. Bogdan Marinescu |
From: John H. <jdh...@ac...> - 2004-07-21 22:47:45
|
>>>>> "Bogdan" == Bogdan Marinescu <bogdanal@b.astral.ro> writes: Bogdan> Hello all, First I want to apologize for sending this Bogdan> message to both lists, but I couldn't decide which one is Bogdan> more appropriate and I would like to have an answer as Bogdan> soon as possible. Hi Bogdan, Either list is appropriate for this kind of post. I think the users is slightly better because then other users who may have seen the error will get the benefit of the post and response. cross-posting will not speed up the response, but may delay it :-) Bogdan> I discovered matplotlib some while ago, but it wasn't Bogdan> until recently when I decided to integrate it into one of Bogdan> my programs, trying to replace PyPlot that doesn't have Bogdan> enough features for me. I downloaded and installed version Bogdan> 0.60.2. Your library seemed perfect, until I discovered Bogdan> some strange problems that prevented me from using it as Bogdan> this point :( Specifically, I'm reffering to the Bogdan> embedding_in_wx2.py example that I found in the 'examples' Bogdan> directory. I modified it as follows (sorry for inlining, Bogdan> but I don't know if the listserver accepts any Bogdan> attachements at all, I didn't use it before): Thanks for your example. I was able to trace the segfault (exposed on win32 but not linux in my tests) to agg.tostring_rgb, as you noted. On window maximizing, there was an uncaught memory allocation error because I was allocating on the stack rather than the heap. This is fixed, and your (modified) example now runs fine for me on win32. I've built a snapshot of matplotlib-0.61 for you and uploaded it to http://nitace.bsd.uchicago.edu:8080/files/share/matplotlib-0.61.0a.win32-py2.3.exe You'll notice a few changes: a new navigation toolbar - see http://sourceforge.net/mailarchive/forum.php?thread_id=5173059&forum_id=36187 for a discussion of the new icons, what they do, and how to get the old toolbar if you want it. As before, you can also just comment out the set_toolbar line to remove the toolbar completely. Also, the event handling mechanism has been changed as part of the process of making this more generic and useful. You now call mpl_connect rather than connect and your callback takes a single argument. For example, if you want to know where the user clicks a mouse on your figure, you define a function def click(event): print 'you clicked', event.x, event.y and register this function with the event handler canvas.mpl_connect('button_press_event', click) Then whenever the user clicks anywhere on the figure canvas, your function will be called and passed a matplotlib.backend_bases.MplEvent instance. The event instance has the following attributes defined. name # the event name canvas # the FigureCanvas instance generating the event x # x position - pixels from left of canvas y # y position - pixels from bottom of canvas button # button pressed None, 1, 2, 3 inaxes # the Axes instance if mouse us over axes xdata # x coord of mouse in data coords ydata # y coord of mouse in data coords You can currently connect to the following events: 'button_press_event', button_release_event', 'motion_notify_event' and I plan to add key press. Below is your example, modified to work with 0.61. I fixed the event handling and took out the onpaint connection from init (this did some funky things in my win32 tests) Let me know how it goes: you're the first 0.61 crash test dummy. Anyone else who wants to try this please feel free - the new toolbar only works with GTK* and WX* in this snapshot. #!/usr/bin/env python """ An example of how to use wx or wxagg in an application w. or w/o the toolbar """ import matplotlib matplotlib.use('WXAgg') from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas from matplotlib.backends.backend_wx import NavigationToolbar2Wx from matplotlib.figure import Figure from matplotlib.numerix import arange, sin, pi from wxPython.wx import * class Cursor: def __init__(self, canvas, ax): self.canvas = canvas self.ax = ax def mouse_move(self, event): x, y = event.x, event.y if event.inaxes: ax = event.inaxes minx, maxx = ax.get_xlim() miny, maxy = ax.get_ylim() print 'x=%1.2f, y=%1.2f'%(event.xdata, event.ydata) class CanvasFrame(wxFrame): def __init__(self): wxFrame.__init__(self,None,-1, 'CanvasFrame',size=( 900,900 ) ) self.SetBackgroundColour(wxNamedColor("WHITE")) self.figure = Figure( ) self.axes = self.figure.add_subplot(111) t = arange(0.0,3.0,0.01) s = sin(2*pi*t) c = sin(4*pi*t) p = self.axes.fill(t,s,'b',t,c,'g') p[ 0 ].set_alpha( 0.2 ) p[ 1 ].set_alpha( 0.2 ) #p = self.axes.fill(t,s,'b') #p[ 0 ].set_alpha( 0.2 ) #p[ 1 ].set_alpha( 0.2 ) #self.axes.plot(t,c,'g') self.axes.vlines( [1.5], -1.0, 1.0 ) self.canvas = FigureCanvas(self, -1, self.figure) self.sizer = wxBoxSizer(wxVERTICAL) self.sizer.Add(self.canvas, 1, wxTOP | wxLEFT | wxEXPAND) self.SetSizer(self.sizer) self.SetAutoLayout( True ) self.sizer.Fit( self ) cursor = Cursor(self.canvas, self.axes) #cursor = SnaptoCursor(canvas, ax, t, s) self.canvas.mpl_connect('motion_notify_event', cursor.mouse_move) self.add_toolbar() # comment this out for no toolbar # Capture the paint message #EVT_PAINT(self, self.OnPaint) EVT_SIZE( self, self.OnSize) def add_toolbar(self): self.toolbar = NavigationToolbar2Wx(self.canvas) self.toolbar.Realize() if wxPlatform == '__WXMAC__': # Mac platform (OSX 10.3, MacPython) does not seem to cope with # having a toolbar in a sizer. This work-around gets the buttons # back, but at the expense of having the toolbar at the top self.SetToolBar(self.toolbar) 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() # 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. self.toolbar.SetSize(wxSize(fw, th)) self.sizer.Add(self.toolbar, 0, wxLEFT | wxEXPAND) # update the axes menu on the toolbar self.toolbar.update() def OnSize( self, event ): print "OnSize" print event.GetSize() event.Skip() # event.Skip() class App(wxApp): def OnInit(self): 'Create the main window and insert the custom frame' frame = CanvasFrame() frame.Show(true) return true app = App(0) app.MainLoop() |