📄 plot.py
字号:
# set font size for every thing but title and legend
dc.SetFont(self._getFont(self._fontSizeAxis))
# sizes axis to axis type, create lower left and upper right corners of plot
if xAxis == None or yAxis == None:
# One or both axis not specified in Draw
p1, p2 = graphics.boundingBox() # min, max points of graphics
if xAxis == None:
xAxis = self._axisInterval(self._xSpec, p1[0], p2[0]) # in user units
if yAxis == None:
yAxis = self._axisInterval(self._ySpec, p1[1], p2[1])
# Adjust bounding box for axis spec
p1[0],p1[1] = xAxis[0], yAxis[0] # lower left corner user scale (xmin,ymin)
p2[0],p2[1] = xAxis[1], yAxis[1] # upper right corner user scale (xmax,ymax)
else:
# Both axis specified in Draw
p1= _Numeric.array([xAxis[0], yAxis[0]]) # lower left corner user scale (xmin,ymin)
p2= _Numeric.array([xAxis[1], yAxis[1]]) # upper right corner user scale (xmax,ymax)
self.last_draw = (graphics, _Numeric.array(xAxis), _Numeric.array(yAxis)) # saves most recient values
# Get ticks and textExtents for axis if required
if self._xSpec is not 'none':
xticks = self._xticks(xAxis[0], xAxis[1])
xTextExtent = dc.GetTextExtent(xticks[-1][1])# w h of x axis text last number on axis
else:
xticks = None
xTextExtent= (0,0) # No text for ticks
if self._ySpec is not 'none':
yticks = self._yticks(yAxis[0], yAxis[1])
if self.getLogScale()[1]:
yTextExtent = dc.GetTextExtent('-2e-2')
else:
yTextExtentBottom = dc.GetTextExtent(yticks[0][1])
yTextExtentTop = dc.GetTextExtent(yticks[-1][1])
yTextExtent= (max(yTextExtentBottom[0],yTextExtentTop[0]),
max(yTextExtentBottom[1],yTextExtentTop[1]))
else:
yticks = None
yTextExtent= (0,0) # No text for ticks
# TextExtents for Title and Axis Labels
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= _Numeric.array([rhsW+lhsW,bottomH+topH]) # make plot area smaller by text size
textSize_shift= _Numeric.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:
self._drawLegend(dc,graphics,rhsW,topH,legendBoxWH, legendSymExt, legendTextExt)
# allow for scaling and shifting plotted points
scale = (self.plotbox_size-textSize_scale) / (p2-p1)* _Numeric.array((1,-1))
shift = -p1*scale + self.plotbox_origin + textSize_shift * _Numeric.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()
self._adjustScrollbars()
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."""
self.last_PointLabel = None #reset pointLabel
dc = wx.BufferedDC(wx.ClientDC(self.canvas), 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
"""
self.last_PointLabel = None #reset maker
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)
def GetClosestPoints(self, pntXY, pointScaled= True):
"""Returns list with
[curveNumber, legend, index of closest point, pointXY, scaledXY, distance]
list for each curve.
Returns [] if no curves are being plotted.
x, y in user coords
if pointScaled == True based on screen coords
if pointScaled == False based on user coords
"""
if self.last_draw == None:
#no graph available
return []
graphics, xAxis, yAxis= self.last_draw
l = []
for curveNum,obj in enumerate(graphics):
#check there are points in the curve
if len(obj.points) == 0:
continue #go to next obj
#[curveNumber, legend, index of closest point, pointXY, scaledXY, distance]
cn = [curveNum]+ [obj.getLegend()]+ obj.getClosestPoint( pntXY, pointScaled)
l.append(cn)
return l
def GetClosetPoint(self, pntXY, pointScaled= True):
"""Returns list with
[curveNumber, legend, index of closest point, pointXY, scaledXY, distance]
list for only the closest curve.
Returns [] if no curves are being plotted.
x, y in user coords
if pointScaled == True based on screen coords
if pointScaled == False based on user coords
"""
#closest points on screen based on screen scaling (pointScaled= True)
#list [curveNumber, index, pointXY, scaledXY, distance] for each curve
closestPts= self.GetClosestPoints(pntXY, pointScaled)
if closestPts == []:
return [] #no graph present
#find one with least distance
dists = [c[-1] for c in closestPts]
mdist = min(dists) #Min dist
i = dists.index(mdist) #index for min dist
return closestPts[i] #this is the closest point on closest curve
def UpdatePointLabel(self, mDataDict):
"""Updates the pointLabel point on screen with data contained in
mDataDict.
mDataDict will be passed to your function set by
SetPointLabelFunc. It can contain anything you
want to display on the screen at the scaledXY point
you specify.
This function can be called from parent window with onClick,
onMotion events etc.
"""
if self.last_PointLabel != None:
#compare pointXY
if _Numeric.sometrue(mDataDict["pointXY"] != self.last_PointLabel["pointXY"]):
#closest changed
self._drawPointLabel(self.last_PointLabel) #erase old
self._drawPointLabel(mDataDict) #plot new
else:
#just plot new with no erase
self._drawPointLabel(mDataDict) #plot new
#save for next erase
self.last_PointLabel = mDataDict
# 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
elif self._dragEnabled and event.LeftIsDown():
coordinates = event.GetPosition()
newpos, oldpos = map(_Numeric.array, map(self.PositionScreenToUser, [coordinates, self._screenCoordinates]))
dist = newpos-oldpos
self._screenCoordinates = coordinates
if self.last_draw is not None:
graphics, xAxis, yAxis= self.last_draw
yAxis -= dist[1]
xAxis -= dist[0]
self._Draw(graphics,xAxis,yAxis)
def OnMouseLeftDown(self,event):
self._zoomCorner1[0], self._zoomCorner1[1]= self._getXY(event)
self._screenCoordinates = _Numeric.array(event.GetPosition())
if self._dragEnabled:
self.SetCursor(self.GrabHandCursor)
self.canvas.CaptureMouse()
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= _Numeric.minimum( self._zoomCorner1, self._zoomCorner2)
maxX, maxY= _Numeric.maximum( self._zoomCorner1, self._zoomCorner2)
self.last_PointLabel = None #reset pointLabel
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) )
if self._dragEnabled:
self.SetCursor(self.HandCursor)
if self.canvas.HasCapture():
self.canvas.ReleaseMouse()
def OnMouseDoubleClick(self,event):
if self._zoomEnabled:
# Give a little time for the click to be totally finished
# before (possibly) removing the scrollbars and trigering
# size events, etc.
wx.FutureCall(200,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
if self.last_PointLabel != None:
self._drawPointLabel(self.last_PointLabel) #erase old
self.last_PointLabel = None
dc = wx.BufferedPaintDC(self.canvas, 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.canvas.GetClientSize()
Size.width = max(1, Size.width)
Size.height = max(1, Size.height)
# 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.EmptyBitmap(Size.width, Size.height)
self._setSize()
self.last_PointLabel = None #reset pointLabel
if self.last_draw is None:
self.Clear()
else:
graphics, xSpec, ySpec = self.last_draw
self._Draw(graphics,xSpec,ySpec)
def OnLeave(self, event):
"""Used to erase pointLabel when mouse outside window"""
if self.last_PointLabel != None:
self._drawPointLabel(self.last_PointLabel) #erase old
self.last_PointLabel = None
def OnScroll(self, evt):
if not self._adjustingSB:
self._sb_ignore = True
sbpos = evt.GetPosition()
if evt.GetOrientation() == wx.VERTICAL:
fullrange,pagesize = self.sb_vert.GetRange(),self.sb_vert.GetPageSize()
sbpos = fullrange-pagesize-sbpos
dist = sbpos*self._sb_yunit-(self._getYCurrentRange()[0]-self._sb_yfullrange[0])
self.ScrollUp(dist)
if evt.GetOrientation() == wx.HORIZONTAL:
dist = sbpos*self._sb_xunit-(self._getXCurrentRange()[0]-self._sb_xfullrange[0])
self.ScrollRight(dist)
# Private Methods **************************************************
def _setSize(self, width=None, height=None):
"""DC width and height."""
if width == None:
(self.width,self.height) = self.canvas.GetClientSize()
else:
self.width, self.height= width,height
self.plotbox_size = 0.97*_Numeric.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 = _Numeric.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 _drawPointLabel(self, mDataDict):
"""Draws and erases pointLabels"""
width = self._Buffer.GetWidth()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -