📄 wxpyplot.py
字号:
titleWH, xLabelWH, yLabelWH= self._titleLablesWH(dc, graphics)
#TextExtents for Legend
legendBoxWH, legendSymExt, legendTextExt = self._legendWH(dc, graphics)
#room around graph area
rhsW= max(xTextExtent[0], legendBoxWH[0]) #use larger of number width or legend width
lhsW= yTextExtent[0]+ yLabelWH[1]
bottomH= max(xTextExtent[1], yTextExtent[1]/2.)+ xLabelWH[1]
topH= yTextExtent[1]/2. + titleWH[1]
textSize_scale= scipy.array([rhsW+lhsW,bottomH+topH]) #make plot area smaller by text size
textSize_shift= scipy.array([lhsW, bottomH]) #shift plot area by this amount
#drawing title and labels text
dc.SetFont(self._getFont(self._fontSizeTitle))
titlePos= (self.plotbox_origin[0]+ lhsW + (self.plotbox_size[0]-lhsW-rhsW)/2.- titleWH[0]/2.,
self.plotbox_origin[1]- self.plotbox_size[1])
dc.DrawText(graphics.getTitle(),titlePos[0],titlePos[1])
dc.SetFont(self._getFont(self._fontSizeAxis))
xLabelPos= (self.plotbox_origin[0]+ lhsW + (self.plotbox_size[0]-lhsW-rhsW)/2.- xLabelWH[0]/2.,
self.plotbox_origin[1]- xLabelWH[1])
dc.DrawText(graphics.getXLabel(),xLabelPos[0],xLabelPos[1])
yLabelPos= (self.plotbox_origin[0],
self.plotbox_origin[1]- bottomH- (self.plotbox_size[1]-bottomH-topH)/2.+ yLabelWH[0]/2.)
if graphics.getYLabel(): #bug fix for Linux
dc.DrawRotatedText(graphics.getYLabel(),yLabelPos[0],yLabelPos[1],90)
#drawing legend makers and text
if self._legendEnabled:
if len(graphics) > self._legendItems:
self._drawLegend(dc,graphics,rhsW,topH,legendBoxWH, legendSymExt, legendTextExt, self._legendItems)
else:
self._drawLegend(dc,graphics,rhsW,topH,legendBoxWH, legendSymExt, legendTextExt)
#allow for scaling and shifting plotted points
scale = (self.plotbox_size-textSize_scale) / (p2-p1)* scipy.array((1,-1))
shift = -p1*scale + self.plotbox_origin + textSize_shift * scipy.array((1,-1))
self._pointScale= scale #make available for mouse events
self._pointShift= shift
self._drawAxes(dc, p1, p2, scale, shift, xticks, yticks)
graphics.scaleAndShift(scale, shift)
graphics.setPrinterScale(self.printerScale) #thicken up lines and markers if printing
#set clipping area so drawing does not occur outside axis box
ptx,pty,rectWidth,rectHeight= self._point2ClientCoord(p1, p2)
dc.SetClippingRegion(ptx,pty,rectWidth,rectHeight)
#Draw the lines and markers
#start = time.clock()
graphics.draw(dc)
#print "entire graphics drawing took: %f second"%(time.clock() - start)
#remove the clipping region
dc.DestroyClippingRegion()
dc.EndDrawing()
def Redraw(self, dc= None):
"""Redraw the existing plot."""
if self.last_draw is not None:
graphics, xAxis, yAxis= self.last_draw
self.Draw(graphics,xAxis,yAxis,dc)
def Clear(self):
"""Erase the window."""
dc = wx.wxBufferedDC(wx.wxClientDC(self), self._Buffer)
dc.Clear()
self.last_draw = None
def Zoom(self, Center, Ratio):
""" Zoom on the plot
Centers on the X,Y coords given in Center
Zooms by the Ratio = (Xratio, Yratio) given
"""
x,y = Center
if self.last_draw != None:
(graphics, xAxis, yAxis) = self.last_draw
w = (xAxis[1] - xAxis[0]) * Ratio[0]
h = (yAxis[1] - yAxis[0]) * Ratio[1]
xAxis = ( x - w/2, x + w/2 )
yAxis = ( y - h/2, y + h/2 )
self.Draw(graphics, xAxis, yAxis)
# event handlers **********************************
def OnMotion(self, event):
if self._zoomEnabled and event.LeftIsDown():
if self._hasDragged:
self._drawRubberBand(self._zoomCorner1, self._zoomCorner2) #remove old
else:
self._hasDragged= True
self._zoomCorner2[0], self._zoomCorner2[1] = self.GetXY(event)
self._drawRubberBand(self._zoomCorner1, self._zoomCorner2) #add new
def OnMouseLeftDown(self,event):
self._zoomCorner1[0], self._zoomCorner1[1]= self.GetXY(event)
def OnMouseLeftUp(self, event):
if self._zoomEnabled:
if self._hasDragged == True:
self._drawRubberBand(self._zoomCorner1, self._zoomCorner2) #remove old
self._zoomCorner2[0], self._zoomCorner2[1]= self.GetXY(event)
self._hasDragged = False #reset flag
minX, minY= scipy.minimum( self._zoomCorner1, self._zoomCorner2)
maxX, maxY= scipy.maximum( self._zoomCorner1, self._zoomCorner2)
if self.last_draw != None:
self.Draw(self.last_draw[0], xAxis = (minX,maxX), yAxis = (minY,maxY), dc = None)
#else: # A box has not been drawn, zoom in on a point
## this interfered with the double click, so I've disables it.
# X,Y = self.GetXY(event)
# self.Zoom( (X,Y), (self._zoomInFactor,self._zoomInFactor) )
def OnMouseDoubleClick(self,event):
if self._zoomEnabled:
self.Reset()
def OnMouseRightDown(self,event):
if self._zoomEnabled:
X,Y = self.GetXY(event)
self.Zoom( (X,Y), (self._zoomOutFactor, self._zoomOutFactor) )
def OnPaint(self, event):
# All that is needed here is to draw the buffer to screen
dc = wx.wxBufferedPaintDC(self, self._Buffer)
def OnSize(self,event):
# The Buffer init is done here, to make sure the buffer is always
# the same size as the Window
Size = self.GetClientSizeTuple()
# Make new offscreen bitmap: this bitmap will always have the
# current drawing in it, so it can be used to save the image to
# a file, or whatever.
self._Buffer = wx.wxEmptyBitmap(Size[0],Size[1])
self._setSize()
if self.last_draw is None:
self.Clear()
else:
graphics, xSpec, ySpec = self.last_draw
self.Draw(graphics,xSpec,ySpec)
#Private Methods **************************************************
def _setSize(self, width=None, height=None):
"""DC width and height."""
if width == None:
(self.width,self.height) = self.GetClientSizeTuple()
else:
self.width, self.height= width,height
self.plotbox_size = 0.97*scipy.array([self.width, self.height])
xo = 0.5*(self.width-self.plotbox_size[0])
yo = self.height-0.5*(self.height-self.plotbox_size[1])
self.plotbox_origin = scipy.array([xo, yo])
def _setPrinterScale(self, scale):
"""Used to thicken lines and increase marker size for print out."""
#line thickness on printer is very thin at 600 dot/in. Markers small
self.printerScale= scale
def _printDraw(self, printDC):
"""Used for printing."""
if self.last_draw != None:
graphics, xSpec, ySpec= self.last_draw
self.Draw(graphics,xSpec,ySpec,printDC)
def _drawLegend(self,dc,graphics,rhsW,topH,legendBoxWH, legendSymExt, legendTextExt, items=None):
"""Draws legend symbols and text"""
#top right hand corner of graph box is ref corner
trhc= self.plotbox_origin+ (self.plotbox_size-[rhsW,topH])*[1,-1]
legendLHS= .091* legendBoxWH[0] #border space between legend sym and graph box
lineHeight= max(legendSymExt[1], legendTextExt[1]) * 1.1 #1.1 used as space between lines
dc.SetFont(self._getFont(self._fontSizeLegend))
if items is None:
length = len(graphics)
else:
length = items
for i in range(length):
o = graphics[i]
s= i*lineHeight
if isinstance(o,PolyMarker):
#draw marker with legend
pnt= (trhc[0]+legendLHS+legendSymExt[0]/2., trhc[1]+s+lineHeight/2.)
o.draw(dc, self.printerScale, coord= scipy.array([pnt]))
elif isinstance(o,PolyLine):
#draw line with legend
pnt1= (trhc[0]+legendLHS, trhc[1]+s+lineHeight/2.)
pnt2= (trhc[0]+legendLHS+legendSymExt[0], trhc[1]+s+lineHeight/2.)
o.draw(dc, self.printerScale, coord= scipy.array([pnt1,pnt2]))
else:
raise TypeError, "object is neither PolyMarker or PolyLine instance"
#draw legend txt
pnt= (trhc[0]+legendLHS+legendSymExt[0], trhc[1]+s+lineHeight/2.-legendTextExt[1]/2)
dc.DrawText(o.getLegend(),pnt[0],pnt[1])
dc.SetFont(self._getFont(self._fontSizeAxis)) #reset
def _titleLablesWH(self, dc, graphics):
"""Draws Title and labels and returns width and height for each"""
#TextExtents for Title and Axis Labels
dc.SetFont(self._getFont(self._fontSizeTitle))
title= graphics.getTitle()
titleWH= dc.GetTextExtent(title)
dc.SetFont(self._getFont(self._fontSizeAxis))
xLabel, yLabel= graphics.getXLabel(),graphics.getYLabel()
xLabelWH= dc.GetTextExtent(xLabel)
yLabelWH= dc.GetTextExtent(yLabel)
return titleWH, xLabelWH, yLabelWH
def _legendWH(self, dc, graphics):
"""Returns the size in screen units for legend box"""
if self._legendEnabled != True:
legendBoxWH= symExt= txtExt= (0,0)
else:
#find max symbol size
symExt= graphics.getSymExtent(self.printerScale)
#find max legend text extent
dc.SetFont(self._getFont(self._fontSizeLegend))
txtList= graphics.getLegendNames()
txtExt= dc.GetTextExtent(txtList[0])
for txt in graphics.getLegendNames()[1:]:
txtExt= scipy.maximum(txtExt,dc.GetTextExtent(txt))
maxW= symExt[0]+txtExt[0]
maxH= max(symExt[1],txtExt[1])
#padding .1 for lhs of legend box and space between lines
maxW= maxW* 1.1
maxH= maxH* 1.1 * len(txtList)
dc.SetFont(self._getFont(self._fontSizeAxis))
legendBoxWH= (maxW,maxH)
return (legendBoxWH, symExt, txtExt)
def _drawRubberBand(self, corner1, corner2):
"""Draws/erases rect box from corner1 to corner2"""
ptx,pty,rectWidth,rectHeight= self._point2ClientCoord(corner1, corner2)
#draw rectangle
dc = wx.wxClientDC( self )
dc.BeginDrawing()
dc.SetPen(wx.wxPen(wx.wxBLACK))
dc.SetBrush(wx.wxBrush( wx.wxWHITE, wx.wxTRANSPARENT ) )
dc.SetLogicalFunction(wx.wxINVERT)
dc.DrawRectangle( ptx,pty,rectWidth,rectHeight)
dc.SetLogicalFunction(wx.wxCOPY)
dc.EndDrawing()
def _getFont(self,size):
"""Take font size, adjusts if printing and returns wxFont"""
s = size*self.printerScale
of = self.GetFont()
#Linux speed up to get font from cache rather than X font server
key = (int(s), of.GetFamily (), of.GetStyle (), of.GetWeight ())
font = self._fontCache.get (key, None)
if font:
return font # yeah! cache hit
else:
font = wx.wxFont(int(s), of.GetFamily(), of.GetStyle(), of.GetWeight())
self._fontCache[key] = font
return font
def _point2ClientCoord(self, corner1, corner2):
"""Converts user point coords to client screen int coords x,y,width,height"""
c1= scipy.array(corner1)
c2= scipy.array(corner2)
#convert to screen coords
pt1= c1*self._pointScale+self._pointShift
pt2= c2*self._pointScale+self._pointShift
#make height and width positive
pul= scipy.minimum(pt1,pt2) #Upper left corner
plr= scipy.maximum(pt1,pt2) #Lower right corner
rectWidth, rectHeight= plr-pul
ptx,pty= pul
return int(ptx),int(pty),int(rectWidth),int(rectHeight) #return ints
def _axisInterval(self, spec, lower, upper):
"""Returns sensible axis range for given spec"""
if spec == 'none' or spec == 'min':
if lower == upper:
return lower-0.5, upper+0.5
else:
return lower, upper
elif spec == 'auto':
range = upper-lower
if range == 0.:
return lower-0.5, upper+0.5
log = scipy.log10(range)
power = scipy.floor(log)
fraction = log-power
if fraction <= 0.05:
power = power-1
grid = 10.**power
lower = lower - lower % grid
mod = upper % grid
if mod != 0:
upper = upper - mod + grid
return lower, upper
elif type(spec) == type(()):
lower, upper = spec
if lower <= upper:
return lower, upper
else:
return upper, lower
else:
raise ValueError, str(spec) + ': illegal axis specification'
def _drawAxes(self, dc, p1, p2, scale, shift, xticks, yticks):
penWidth= self.printerScale #increases thickness for printing only
dc.SetPen(wx.wxPen(wx.wxNamedColour('BLACK'),int(penWidth)))
#set length of tick marks--long ones make grid
if self._gridEnabled:
x,y,width,height= self._point2ClientCoord(p1,p2)
yTickLength= width/2.0 +1
xTickLength= height/2.0 +1
else:
yTickLength= 3 * self.printerScale #lengthens lines for printing
xTickLength= 3 * self.printerScale
if self._xSpec is not 'none':
lower, upper = p1[0],p2[0]
text = 1
for y, d in [(p1[1], -xTickLength), (p2[1], xTickLength)]: #miny, maxy and tick lengths
a1 = scale*scipy.array([lower, y])+shift
a2 = scale*scipy.array([upper, y])+shift
dc.DrawLine(a1[0],a1[1],a2[0],a2[1]) #draws upper and lower axis line
for x, label in xticks:
pt = scale*scipy.array([x, y])+shift
dc.DrawLine(pt[0],pt[1],pt[0],pt[1] + d) #draws tick mark d units
if text:
dc.DrawText(label,pt[0],pt[1])
text = 0 #axis values not drawn on top side
if self._ySpec is not 'none':
lower, upper = p1[1],p2[1]
text = 1
h = dc.GetCharHeight()
for x, d in [(p1[0], -yTickLength), (p2[0], yTickLength)]:
a1 = scale*scipy.array([x, lower])+shift
a2 = scale*scipy.array([x, upper])+shift
dc.DrawLine(a1[0],a1[1],a2[0],a2[1])
for y, label in yticks:
pt = scale*scipy.array([x, y])+shift
dc.DrawLine(pt[0],pt[1],pt[0]-d,pt[1])
if text:
dc.DrawText(label,pt[0]-dc.GetTextExtent(label)[0],
pt[1]-0.5*h)
text = 0 #axis values not drawn on right side
def _ticks(self, lower, upper):
ideal = (upper-lower)/7.
log = scipy.log10(ideal)
power = scipy.floor(log)
fraction = log-power
factor = 1.
error = fraction
for f, lf in self._multiples:
e = scipy.fabs(fraction-lf)
if e < error:
error = e
factor = f
grid = factor * 10.**power
if power > 4 or power < -4:
format = '%+7.1e'
elif power >= 0:
digits = max(1, int(power))
format = '%' + `digits`+'.0f'
else:
digits = -int(power)
format = '%'+`digits+2`+'.'+`digits`+'f'
ticks = []
t = -grid*scipy.floor(-lower/grid)
while t <= upper:
ticks.append( (t, format % (t,)) )
t = t + grid
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -