Subclassing PyPlot, need some help
Andriy
basilisk96 at gmail.com
Fri Aug 3 11:49:25 PDT 2007
> The easiest option would be to create a wx.MemoryDC for each subplot,
> then draw each resulting bitmap to the main DC where you want them.
>
> However, while this would work great on screen, it wouldn't work so well
> for printing, as you'd be printing bitmaps, rather than vector graphics.
> It may end up looking OK, if you get the resolutions right, but it feels
> kludgy.
>
> Better options:
>
> Maybe the best way (and the most work) would be to move to
> wx.GraphicsContext, rather than DC -- then you could take advantage of
> transforms to put your plots where and at what size you want them.
>
> Easier: You can probably use a combination of:
>
> wx.DC.SetDeviceOrigin
> wx.DC.SetUserScale
>
> setting those parameters differently, you may be able draw each subplot
> where you want, all with the same DC. UserScale has issues, as DCs are
> all integer based, but you may even be able to do without that. Really,
> setting the Origin should be all you need to draw your subplots where
> you want, as long as the Drawing code doesn't use the size of the DC to
> scale and position things.
>
> Hope that helps,
>
> -Chris
Thanks guys for all the suggestions, they made this little problem
more interesting. I've trashed the idea of keeping a list of
PlotCanvas instances for the subplots, and instead I just maintain a
list of the subplot parameters that are used to draw them (graphics
objects, axes, etc.)
After digging deeper into PlotCanvas drawing code, I discovered two
attributes that govern the size and position of the plot on the
canvas: plotbox_size and plotbox_origin. I exploited these by:
1) overriding the OnSize handler and recalculating them,
2) overriding the _Draw method, where I traverse the list of graphics
parameters, and draw each plot at its proper position and size on the
canvas. Nothing special, really - just using a wx.BufferedDC onto a
ClientDC and a bitmap. All that has worked out very well... almost :)
It looks excellent on screen and saves the canvas to an image file as
expected. The last hurdle now is printing: it seems to be scaling
incorrectly (looks at least twice as large as it should be, and the
print preview image varies in size depending on the zoom percentage
selected). Maybe I need to override a few more methods to make this
work completely? We'll see.
Here's my code so far:
<<CODE START>>
import wx
import string as _string
import numpy as _N
from wx.lib.plot import PlotCanvas, PlotGraphics, PlotPrintout,
PolyLine, PolyMarker
class FlexPlot(PlotCanvas):
def __init__(self, parent, rows=1, cols=1, title=''):
self.parent = parent
# Subplots setup
if rows < 1: rows = 1
if cols < 1: cols = 1
self.rows = rows
self.cols = cols
self.Title = title
self.numplots = self.rows * self.cols
self.plotArgs = [None] * self.numplots
#now initialize the superclass
PlotCanvas.__init__(self, parent)
def OnSize(self, event):
canvasSize = self.canvas.GetClientSize()
self._Buffer = wx.EmptyBitmap(canvasSize.width, canvasSize.height)
self._setSize()
canvasPlotBoxSize, canvasOrigin = self.plotbox_size, self.plotbox_origin
self.SubPlotSize = (canvasPlotBoxSize[0]/self.cols,
canvasPlotBoxSize[1]/self.rows)
self.plotbox_size = _N.array(self.SubPlotSize)
size = self.plotbox_size
#reference origin for all subplots from top left corner of canvas
self.canvasOrigin = canvasOrigin - size * _N.array((0, self.rows-1))
self._Draw()
if event is not None:
event.Skip()
def OnPaint(self, event):
PlotCanvas.OnPaint(self, event)
event.Skip()
def _Draw(self, graphics=None, xAxis = None, yAxis = None, dc = None):
if dc == None:
#set new dc and clear it
dc = wx.BufferedDC(wx.ClientDC(self.canvas), self._Buffer)
dc.Clear()
size = self.plotbox_size
canvasOrigin = self.canvasOrigin
for i, subPlotArgs in enumerate(self.plotArgs):
if subPlotArgs is None:
self.Clear()
else:
#tblPos is a custom function; returns row, col
position based on index i
r, c = tblPos(self.rows, self.cols, i)
offsets = _N.array((c, r)) #switch: column is X, row is Y
self.plotbox_origin = canvasOrigin + offsets * size
graphics, xSpec, ySpec, gridSpec, legendSpec = subPlotArgs
PlotCanvas._Draw(self, graphics, xSpec, ySpec, dc=dc)
#disable scrollbars
self.sb_vert.Show(False)
self.sb_hor.Show(False)
def Plot(self, graphics, index=0, xlim=None, ylim=None,
grid=False, legend=False):
self.plotArgs[index] = [graphics, xlim, ylim, grid, legend]
self.resetDefaults(self)
self.Draw(graphics, xAxis=xlim, yAxis=ylim)
self.SetEnableGrid(grid)
self.SetEnableLegend(legend)
def SaveFile(self, fileName=''):
"""Saves the current plot and subplots to an image file."""
#Delegate to superclass.
res = PlotCanvas.SaveFile(self, fileName)
return res
def Printout(self, paper=None):
"""Print current plot."""
#Delegate to superclass.
PlotCanvas.Printout(self, paper=None)
def resetDefaults(self, client):
"""Just to reset the fonts back to customized defaults"""
client.SetGridColour('light gray')
client.SetFont(wx.Font(10,wx.SWISS,wx.NORMAL,wx.NORMAL))
client.SetFontSizeAxis(10)
client.SetFontSizeLegend(7)
client.SetXSpec('auto')
client.SetYSpec('auto')
def _printDraw(self, printDC):
"""Used for printing."""
#not sure yet how to fix this one...
self._Draw(dc=printDC)
<<CODE END>>
Phew!
-amv
More information about the wxpython-users
mailing list