📄 plot.py
字号:
#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 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= _numpy.minimum( self._zoomCorner1, self._zoomCorner2) maxX, maxY= _numpy.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) ) 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 if self.last_PointLabel != None: self._drawPointLabel(self.last_PointLabel) #erase old self.last_PointLabel = None dc = wx.BufferedPaintDC(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.GetClientSize() # 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[0],Size[1]) 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 # Private Methods ************************************************** def _setSize(self, width=None, height=None): """DC width and height.""" if width == None: (self.width,self.height) = self.GetClientSize() else: self.width, self.height= width,height self.plotbox_size = 0.97*_numpy.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 = _numpy.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() height = self._Buffer.GetHeight() tmp_Buffer = wx.EmptyBitmap(width,height) dcs = wx.MemoryDC() dcs.SelectObject(tmp_Buffer) dcs.Clear() dcs.BeginDrawing() self._pointLabelFunc(dcs,mDataDict) #custom user pointLabel function dcs.EndDrawing() dc = wx.ClientDC( self ) #this will erase if called twice dc.Blit(0, 0, width, height, dcs, 0, 0, wx.EQUIV) #(NOT src) XOR dst def _drawLegend(self,dc,graphics,rhsW,topH,legendBoxWH, legendSymExt, legendTextExt): """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)) for i in range(len(graphics)): 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= _numpy.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= _numpy.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= _numpy.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.ClientDC( self ) dc.BeginDrawing() dc.SetPen(wx.Pen(wx.BLACK)) dc.SetBrush(wx.Brush( wx.WHITE, wx.TRANSPARENT ) ) dc.SetLogicalFunction(wx.INVERT) dc.DrawRectangle( ptx,pty, rectWidth,rectHeight) dc.SetLogicalFunction(wx.COPY) dc.EndDrawing() def _getFont(self,size): """Take font size, adjusts if printing and returns wx.Font""" 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.Font(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= _numpy.array(corner1) c2= _numpy.array(corner2) # convert to screen coords pt1= c1*self._pointScale+self._pointShift pt2= c2*self._pointScale+self._pointShift # make height and width positive pul= _numpy.minimum(pt1,pt2) # Upper left corner plr= _numpy.maximum(pt1,pt2) # Lower right corner rectWidth, rectHeight= plr-pul ptx,pty= pul return ptx, pty, rectWidth, rectHeight 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.: if abs(range) < 1e-36: return lower-0.5, upper+0.5 log = _numpy.log10(range) power = _numpy.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.Pen(wx.NamedColour('BLACK'), 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*_numpy.array([lower, y])+shift a2 = scale*_numpy.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*_numpy.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*_numpy.array([x, lower])+shift a2 = scale*_numpy.array([x, upper])+shift dc.DrawLine(a1[0],a1[1],a2[0],a2[1]) for y, label in yticks: pt = scale*_numpy.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, step=None): ideal = (upper-lower)/7. log = _numpy.log10(ideal) power = _numpy.floor(log) fraction = log-power factor = 1. error = fraction for f, lf in self._multiples: e = _numpy.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' #force grid when step is not None if step is not None: grid = step ticks = [] t = -grid*_numpy.floor(-lower/grid) while t <= upper: if t == -0: t = 0 #remove neg zero condition ticks.append( (t, format % (t,)) ) t = t + grid return ticks def _scope_ticks (self, lower, upper): '''Always 10 divisions, no labels''' grid = (upper - lower) / 10.0 ticks = [] t = lower while t <= upper:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -