def _drawPlotLine(self, painter, xvals, yvals, posn, xdata, ydata, cliprect): """Draw the line connecting the points.""" pts = self._getLinePoints(xvals, yvals, posn, xdata, ydata) if len(pts) < 2: return s = self.settings if not s.FillBelow.hide: # construct polygon to draw filled region polypts = qt4.QPolygonF([qt4.QPointF(pts[0].x(), posn[3])]) polypts += pts polypts.append(qt4.QPointF(pts[len(pts) - 1].x(), posn[3])) utils.brushExtFillPolygon(painter, s.FillBelow, cliprect, polypts) if not s.FillAbove.hide: polypts = qt4.QPolygonF([qt4.QPointF(pts[0].x(), posn[1])]) polypts += pts polypts.append(qt4.QPointF(pts[len(pts) - 1].x(), posn[1])) utils.brushExtFillPolygon(painter, s.FillAbove, cliprect, polypts) # draw line between points if not s.PlotLine.hide: painter.setPen(s.PlotLine.makeQPen(painter)) utils.plotClippedPolyline(painter, cliprect, pts)
def areaDrawStacked(self, painter, posns, maxwidth, dsvals, axes, widgetposn, clip): """Draw a stacked area plot""" s = self.settings # get axis which values are plotted along ishorz = s.direction == 'horizontal' vaxis = axes[not ishorz] # compute stacked coordinates stackedvals, stackedcoords = self.calcStackedPoints( dsvals, vaxis, widgetposn) # coordinates of origin zerocoords = vaxis.dataToPlotterCoords(widgetposn, N.zeros(posns.shape)) # draw areas (reverse order, so edges are plotted correctly) for dsnum, coords in izip(xrange(len(stackedcoords) - 1, -1, -1), stackedcoords[::-1]): # add points at end to make polygon p1 = N.hstack([[zerocoords[0]], coords, [zerocoords[-1]]]) p2 = N.hstack([[posns[0]], posns, [posns[-1]]]) # construct polygon on path, clipped poly = qt4.QPolygonF() if ishorz: utils.addNumpyToPolygonF(poly, p1, p2) else: utils.addNumpyToPolygonF(poly, p2, p1) clippoly = qt4.QPolygonF() utils.polygonClip(poly, clip, clippoly) path = qt4.QPainterPath() path.addPolygon(clippoly) path.closeSubpath() # actually draw polygon brush = s.BarFill.get('fills').returnBrushExtended(dsnum) utils.brushExtFillPath(painter, brush, path) # now draw lines poly = qt4.QPolygonF() if ishorz: utils.addNumpyToPolygonF(poly, coords, posns) else: utils.addNumpyToPolygonF(poly, posns, coords) pen = s.BarLine.get('lines').makePen(painter, dsnum) painter.setPen(pen) utils.plotClippedPolyline(painter, clip, poly) # draw error bars barwidth = maxwidth * s.barfill for barval, dsval in izip(stackedvals, dsvals): self.drawErrorBars(painter, posns, barwidth, barval, dsval, axes, widgetposn)
def _plotLine(self, painter, xpts, ypts, bounds, clip): """ Plot the points in xpts, ypts.""" x1, y1, x2, y2 = bounds maxdeltax = (x2 - x1) * 3 / 4 maxdeltay = (y2 - y1) * 3 / 4 # idea is to collect points until we go out of the bounds # or reach the end, then plot them pts = qt4.QPolygonF() lastx = lasty = -65536 for x, y in itertools.izip(xpts, ypts): # ignore point if it outside sensible bounds if x < -32767 or y < -32767 or x > 32767 or y > 32767: if len(pts) >= 2: utils.plotClippedPolyline(painter, clip, pts) pts.clear() else: # if the jump wasn't too large, add the point to the points if abs(x - lastx) < maxdeltax and abs(y - lasty) < maxdeltay: pts.append(qt4.QPointF(x, y)) else: # draw what we have until now, and start a new line if len(pts) >= 2: utils.plotClippedPolyline(painter, clip, pts) pts.clear() pts.append(qt4.QPointF(x, y)) lastx = x lasty = y # draw remaining points if len(pts) >= 2: utils.plotClippedPolyline(painter, clip, pts)
def drawFillPts(self, painter, brushext, cliprect, ptsx, ptsy): '''Draw points for plotting a fill.''' pts = qt4.QPolygonF() utils.addNumpyToPolygonF(pts, ptsx, ptsy) filltype = brushext.filltype # this is broken: FIXME if filltype == 'left': dyend = ptsy[-1]-self._box[1] pts.append( qt4.QPointF(ptsx[-1]-dyend*tan30, self._box[1]) ) dystart = ptsy[0]-self._box[1] pts.append( qt4.QPointF(ptsx[0]-dystart*tan30, self._box[1]) ) elif filltype == 'right': pts.append( qt4.QPointF(self._box[2], ptsy[-1]) ) pts.append( qt4.QPointF(self._box[2], ptsy[0]) ) elif filltype == 'bottom': dyend = self._box[3]-ptsy[-1] pts.append( qt4.QPointF(ptsx[-1]-dyend*tan30, self._box[3]) ) dystart = self._box[3]-ptsy[0] pts.append( qt4.QPointF(ptsx[0]-dystart*tan30, self._box[3]) ) elif filltype == 'polygon': pass else: pts = None if pts is not None: utils.brushExtFillPolygon(painter, brushext, cliprect, pts)
def _errorBarsFilled(style, xmin, xmax, ymin, ymax, xplotter, yplotter, s, painter, clip): """Draw filled region as error region.""" ptsabove = qt4.QPolygonF() ptsbelow = qt4.QPolygonF() hidevert = True # keep track of what's shown hidehorz = True if ('vert' in style and (ymin is not None and ymax is not None) and not s.ErrorBarLine.hideVert): hidevert = False # lines above/below points utils.addNumpyToPolygonF(ptsbelow, xplotter, ymin) utils.addNumpyToPolygonF(ptsabove, xplotter, ymax) elif ('horz' in style and (xmin is not None and xmax is not None) and not s.ErrorBarLine.hideHorz): hidehorz = False # lines left/right points utils.addNumpyToPolygonF(ptsbelow, xmin, yplotter) utils.addNumpyToPolygonF(ptsabove, xmax, yplotter) # draw filled regions above/left and below/right if 'fill' in style and not (hidehorz and hidevert): # construct points for error bar regions retnpts = qt4.QPolygonF() utils.addNumpyToPolygonF(retnpts, xplotter[::-1], yplotter[::-1]) # polygons consist of lines joining the points and continuing # back along the plot line (retnpts) if not s.FillBelow.hideerror: utils.brushExtFillPolygon(painter, s.FillBelow, clip, ptsbelow + retnpts, ignorehide=True) if not s.FillAbove.hideerror: utils.brushExtFillPolygon(painter, s.FillAbove, clip, ptsabove + retnpts, ignorehide=True) # draw optional line (on top of fill) utils.plotClippedPolyline(painter, clip, ptsabove) utils.plotClippedPolyline(painter, clip, ptsbelow)
def brushExtFillPolygon(painter, extbrush, cliprect, polygon, ignorehide=False): """Fill a polygon with an extended brush.""" clipped = qt4.QPolygonF() polygonClip(polygon, cliprect, clipped) path = qt4.QPainterPath() path.addPolygon(clipped) brushExtFillPath(painter, extbrush, path, ignorehide=ignorehide)
def _fillRegion(self, painter, pxpts, pypts, bounds, belowleft, clip, brush): """Fill the region above/below or left/right of the points. belowleft fills below if the variable is 'x', or left if 'y' otherwise it fills above/right.""" # find starting and ending points for the filled region x1, y1, x2, y2 = bounds pts = qt4.QPolygonF() if self.settings.variable == 'x': if belowleft: pts.append(qt4.QPointF(pxpts[0], y2)) endpt = qt4.QPointF(pxpts[-1], y2) else: pts.append(qt4.QPointF(pxpts[0], y1)) endpt = qt4.QPointF(pxpts[-1], y1) else: if belowleft: pts.append(qt4.QPointF(x1, pypts[0])) endpt = qt4.QPointF(x1, pypts[-1]) else: pts.append(qt4.QPointF(x2, pypts[0])) endpt = qt4.QPointF(x2, pypts[-1]) # add the points between utils.addNumpyToPolygonF(pts, pxpts, pypts) # stick on the ending point pts.append(endpt) # draw the clipped polygon clipped = qt4.QPolygonF() utils.polygonClip(pts, clip, clipped) path = qt4.QPainterPath() path.addPolygon(clipped) utils.brushExtFillPath(painter, brush, path)
def draw(self, posn, phelper, outerbounds=None): """Plot the data on a plotter.""" s = self.settings # exit if hidden if s.hide: return # get points in plotter coordinates xp, yp = self._getPlotterCoords(posn) if xp is None or yp is None: # we can't calculate coordinates return x1, y1, x2, y2 = posn cliprect = qt4.QRectF(qt4.QPointF(x1, y1), qt4.QPointF(x2, y2)) painter = phelper.painter(self, posn, clip=cliprect) pen = s.Line.makeQPenWHide(painter) pw = pen.widthF() * 2 lineclip = qt4.QRectF(qt4.QPointF(x1 - pw, y1 - pw), qt4.QPointF(x2 + pw, y2 + pw)) # this is a hack as we generate temporary fake datasets path = qt4.QPainterPath() for xvals, yvals in document.generateValidDatasetParts( document.Dataset(xp), document.Dataset(yp)): poly = qt4.QPolygonF() utils.addNumpyToPolygonF(poly, xvals.data, yvals.data) clippedpoly = qt4.QPolygonF() utils.polygonClip(poly, lineclip, clippedpoly) path.addPolygon(clippedpoly) path.closeSubpath() utils.brushExtFillPath(painter, s.Fill, path, stroke=pen)
def drawGraph(self, painter, bounds, datarange, outerbounds=None): '''Plot graph area and axes.''' s = self.settings xw, yw = bounds[2]-bounds[0], bounds[3]-bounds[1] d60 = 60./180.*math.pi ang = math.atan2(yw, xw/2.) if ang > d60: # taller than wider widthh = xw/2 height = math.tan(d60) * widthh else: # wider than taller height = yw widthh = height / math.tan(d60) # box for equilateral triangle self._box = ( (bounds[2]+bounds[0])/2 - widthh, (bounds[1]+bounds[3])/2 - height/2, (bounds[2]+bounds[0])/2 + widthh, (bounds[1]+bounds[3])/2 + height/2 ) self._width = widthh*2 self._height = height # triangle shaped polygon for graph self._tripoly = p = qt4.QPolygonF() p.append( qt4.QPointF(self._box[0], self._box[3]) ) p.append( qt4.QPointF(self._box[0]+widthh, self._box[1]) ) p.append( qt4.QPointF(self._box[2], self._box[3]) ) path = qt4.QPainterPath() path.addPolygon(p) path.closeSubpath() utils.brushExtFillPath(painter, s.Background, path, stroke=s.Border.makeQPenWHide(painter)) # work out origins and size self._size = max(min(s.fracsize, 1.), 0.) # make sure we don't go past the ends of the allowed range # value of origin of left axis at top self._orgleft = min(s.originleft, 1.-self._size) # value of origin of bottom axis at left self._orgbot = min(s.originbottom, 1.-self._size) # origin of right axis at bottom self._orgright = 1. - self._orgleft - (self._orgbot + self._size)
def getPreviewPixmap(self, ds): """Get a preview pixmap for a dataset.""" size = (140, 70) if ds.dimensions != 1 or ds.datatype != "numeric": return None pixmap = qt4.QPixmap(*size) pixmap.fill(qt4.Qt.transparent) p = qt4.QPainter(pixmap) p.setRenderHint(qt4.QPainter.Antialiasing) # calculate data points try: if len(ds.data) < size[1]: y = ds.data else: intvl = len(ds.data) / size[1] + 1 y = ds.data[::intvl] x = N.arange(len(y)) # plot data points on image minval, maxval = N.nanmin(y), N.nanmax(y) y = (y - minval) / (maxval - minval) * size[1] finite = N.isfinite(y) x, y = x[finite], y[finite] x = x * (1. / len(x)) * size[0] poly = qt4.QPolygonF() utils.addNumpyToPolygonF(poly, x, size[1] - y) p.setPen(qt4.QPen(qt4.Qt.blue)) p.drawPolyline(poly) # draw x axis if span 0 p.setPen(qt4.QPen(qt4.Qt.black)) if minval <= 0 and maxval > 0: y0 = size[1] - (0 - minval) / (maxval - minval) * size[1] p.drawLine(x[0], y0, x[-1], y0) else: p.drawLine(x[0], size[1], x[-1], size[1]) p.drawLine(x[0], 0, x[0], size[1]) except (ValueError, ZeroDivisionError): # zero sized array after filtering or min == max, so return None p.end() return None p.end() return pixmap
def drawFillPts(self, painter, extfill, cliprect, ptsx, ptsy): '''Draw points for plotting a fill.''' pts = qt4.QPolygonF() utils.addNumpyToPolygonF(pts, ptsx, ptsy) filltype = extfill.filltype if filltype == 'center': pts.append(qt4.QPointF(self._xc, self._yc)) utils.brushExtFillPolygon(painter, extfill, cliprect, pts) elif filltype == 'outside': pp = qt4.QPainterPath() pp.moveTo(self._xc, self._yc) pp.arcTo(cliprect, 0, 360) pp.addPolygon(pts) utils.brushExtFillPath(painter, extfill, pp) elif filltype == 'polygon': utils.brushExtFillPolygon(painter, extfill, cliprect, pts)
def _getLinePoints(self, xvals, yvals, posn, xdata, ydata): """Get the points corresponding to the line connecting the points.""" pts = qt4.QPolygonF() s = self.settings steps = s.PlotLine.steps # simple continuous line if steps == 'off': utils.addNumpyToPolygonF(pts, xvals, yvals) # stepped line, with points on left elif steps[:4] == 'left': x1 = xvals[:-1] x2 = xvals[1:] y1 = yvals[:-1] y2 = yvals[1:] utils.addNumpyToPolygonF(pts, x1, y1, x2, y1, x2, y2) # stepped line, with points on right elif steps[:5] == 'right': x1 = xvals[:-1] x2 = xvals[1:] y1 = yvals[:-1] y2 = yvals[1:] utils.addNumpyToPolygonF(pts, x1, y1, x1, y2, x2, y2) # stepped line, with points in centre # this is complex as we can't use the mean of the plotter coords, # as the axis could be log elif steps[:6] == 'centre': axes = self.parent.getAxes((s.xAxis, s.yAxis)) if xdata.hasErrors(): # Special case if error bars on x points: # here we use the error bars to define the steps xmin, xmax = xdata.getPointRanges() # this is duplicated from drawing error bars: bad # convert xmin and xmax to graph coordinates xmin = axes[0].dataToPlotterCoords(posn, xmin) xmax = axes[0].dataToPlotterCoords(posn, xmax) utils.addNumpyToPolygonF(pts, xmin, yvals, xmax, yvals) else: # we put the bin edges half way between the points # we assume this is the correct thing to do even in log space x1 = xvals[:-1] x2 = xvals[1:] y1 = yvals[:-1] y2 = yvals[1:] xc = 0.5 * (x1 + x2) utils.addNumpyToPolygonF(pts, x1, y1, xc, y1, xc, y2) if len(xvals) > 0: pts.append(qt4.QPointF(xvals[-1], yvals[-1])) else: assert False return pts
try: self.checker.check(s.function, s.variable) except RuntimeError, e: self.logEvalError(e) return x1, y1, x2, y2 = posn cliprect = qt4.QRectF( qt4.QPointF(x1, y1), qt4.QPointF(x2, y2) ) painter = phelper.painter(self, posn) self.parent.setClip(painter, posn) apts, bpts = self.getFunctionPoints() px, py = self.parent.graphToPlotCoords(apts, bpts) # plot line painter.setBrush(qt4.QBrush()) painter.setPen( s.PlotLine.makeQPenWHide(painter) ) for x, y in utils.validLinePoints(px, py): if not s.Fill1.hide: self.parent.drawFillPts(painter, s.Fill1, cliprect, x, y) if not s.Fill2.hide: self.parent.drawFillPts(painter, s.Fill2, cliprect, x, y) if not s.PlotLine.hide: p = qt4.QPolygonF() utils.addNumpyToPolygonF(p, x, y) painter.setBrush(qt4.QBrush()) painter.setPen( s.PlotLine.makeQPen(painter) ) utils.plotClippedPolyline(painter, cliprect, p) document.thefactory.register( NonOrthFunction )