def swaplines(self, painter, a1, b1, a2, b2): """Multiline version of swapline where a1, b1, a2, b2 are arrays.""" if self.settings.direction == 'horizontal': a = (a1, b1, a2, b2) else: a = (b1, a1, b2, a2) utils.plotLinesToPainter(painter, a[0], a[1], a[2], a[3])
def _errorBarsBar(style, xmin, xmax, ymin, ymax, xplotter, yplotter, s, painter, clip): """Draw bar style error lines.""" # vertical error bars if ymin is not None and ymax is not None and not s.ErrorBarLine.hideVert: utils.plotLinesToPainter(painter, xplotter, ymin, xplotter, ymax, clip) # horizontal error bars if xmin is not None and xmax is not None and not s.ErrorBarLine.hideHorz: utils.plotLinesToPainter(painter, xmin, yplotter, xmax, yplotter, clip)
def _errorBarsEnds(style, xmin, xmax, ymin, ymax, xplotter, yplotter, s, painter, clip): """Draw perpendiclar ends on error bars.""" size = s.get("markerSize").convert(painter) * s.ErrorBarLine.endsize if ymin is not None and ymax is not None and not s.ErrorBarLine.hideVert: utils.plotLinesToPainter(painter, xplotter - size, ymin, xplotter + size, ymin, clip) utils.plotLinesToPainter(painter, xplotter - size, ymax, xplotter + size, ymax, clip) if xmin is not None and xmax is not None and not s.ErrorBarLine.hideHorz: utils.plotLinesToPainter(painter, xmin, yplotter - size, xmin, yplotter + size, clip) utils.plotLinesToPainter(painter, xmax, yplotter - size, xmax, yplotter + size, clip)
def _errorBarsEnds(style, xmin, xmax, ymin, ymax, xplotter, yplotter, s, painter, clip): """Draw perpendiclar ends on error bars.""" size = (s.get('markerSize').convert(painter) * s.ErrorBarLine.endsize) if ymin is not None and ymax is not None and not s.ErrorBarLine.hideVert: utils.plotLinesToPainter(painter, xplotter - size, ymin, xplotter + size, ymin, clip) utils.plotLinesToPainter(painter, xplotter - size, ymax, xplotter + size, ymax, clip) if xmin is not None and xmax is not None and not s.ErrorBarLine.hideHorz: utils.plotLinesToPainter(painter, xmin, yplotter - size, xmin, yplotter + size, clip) utils.plotLinesToPainter(painter, xmax, yplotter - size, xmax, yplotter + size, clip)
def dataDraw(self, painter, axes, posn, cliprect): """Draw the widget.""" s = self.settings d = self.document # ignore non existing datasets try: data1 = d.data[s.data1] data2 = d.data[s.data2] except KeyError: return # require 2d datasets if data1.dimensions != 2 or data2.dimensions != 2: return # get base length (ensure > 0) baselength = max(s.get('baselength').convert(painter), 1e-6) # try to be nice if the datasets don't match data1st, data2nd = data1.data, data2.data xw = min(data1st.shape[1], data2nd.shape[1]) yw = min(data1st.shape[0], data2nd.shape[0]) # construct indices into datasets yvals, xvals = N.mgrid[0:yw, 0:xw] # convert using 1st dataset to axes values xdsvals, ydsvals = data1.indexToPoint(xvals.ravel(), yvals.ravel()) # convert using axes to plotter values xplotter = axes[0].dataToPlotterCoords(posn, xdsvals) yplotter = axes[1].dataToPlotterCoords(posn, ydsvals) pen = s.Line.makeQPenWHide(painter) painter.setPen(pen) if s.mode == 'cartesian': dx = (data1st[:yw, :xw] * baselength).ravel() dy = (data2nd[:yw, :xw] * baselength).ravel() elif s.mode == 'polar': r = data1st[:yw, :xw].ravel() * baselength theta = data2nd[:yw, :xw].ravel() dx = r * N.cos(theta) dy = r * N.sin(theta) x1, x2 = xplotter-dx, xplotter+dx y1, y2 = yplotter+dy, yplotter-dy if s.arrowfront == 'none' and s.arrowback == 'none': utils.plotLinesToPainter(painter, x1, y1, x2, y2, cliprect) else: arrowsize = s.get('arrowsize').convert(painter) painter.setBrush( s.get('Fill').makeQBrushWHide() ) # this is backward - have to convert from dx, dy to angle, length angles = 180 - N.arctan2(dy, dx) * (180./N.pi) lengths = N.sqrt(dx**2+dy**2) * 2 # scale arrow heads by arrow length if requested if s.scalearrow: arrowsizes = (arrowsize/baselength/2) * lengths else: arrowsizes = N.zeros(lengths.shape) + arrowsize for x, y, l, a, asize in itertools.izip(x2, y2, lengths, angles, arrowsizes): if l != 0.: utils.plotLineArrow(painter, x, y, l, a, asize, arrowleft=s.arrowfront, arrowright=s.arrowback)
def _drawTickSet(self, painter, tickSetn, gridSetn, tickbot, tickleft, tickright, tickLabelSetn=None, labelSetn=None): '''Draw a set of ticks (major or minor). tickSetn: tick setting to get line details gridSetn: setting for grid line (if any) tickXXX: tick arrays for each axis tickLabelSetn: setting used to label ticks, or None if minor ticks labelSetn: setting for labels, if any ''' # this is mostly a lot of annoying trigonometry # compute line ends for ticks and grid lines tl = tickSetn.get('length').convert(painter) mv = self._maxVal() reverse = bool(self.settings.reverse) revsign = [1, -1][reverse] # bottom ticks x1 = (tickbot - self._orgbot)/self._size*self._width + self._box[0] y1 = self._box[3] + N.zeros(x1.shape) x2 = x1 - revsign * tl * sin30 y2 = y1 + tl * cos30 tickbotline = (x1, y1, x2, y2) # bottom grid (removing lines at edge of plot) scaletick = 1 - (tickbot-self._orgbot)/self._size gx = x1 + scaletick*self._width*sin30 gy = y1 - scaletick*self._width*cos30 ne = (scaletick > 1e-6) & (scaletick < (1-1e-6)) gridbotline = (x1[ne], y1[ne], gx[ne], gy[ne]) # left ticks x1 = -(tickleft - self._orgleft)/self._size*self._width*sin30 + ( self._box[0] + self._box[2])*0.5 y1 = (tickleft - self._orgleft)/self._size*self._width*cos30 + self._box[1] if reverse: x2 = x1 - tl y2 = y1 else: x2 = x1 - tl * sin30 y2 = y1 - tl * cos30 tickleftline = (x1, y1, x2, y2) # left grid scaletick = 1 - (tickleft-self._orgleft)/self._size gx = x1 + scaletick*self._width*sin30 gy = self._box[3] + N.zeros(y1.shape) ne = (scaletick > 1e-6) & (scaletick < (1-1e-6)) gridleftline = (x1[ne], y1[ne], gx[ne], gy[ne]) # right ticks x1 = -(tickright - self._orgright)/self._size*self._width*sin30+self._box[2] y1 = -(tickright - self._orgright)/self._size*self._width*cos30+self._box[3] if reverse: x2 = x1 + tl * sin30 y2 = y1 - tl * cos30 else: x2 = x1 + tl y2 = y1 tickrightline = (x1, y1, x2, y2) # right grid scaletick = 1 - (tickright-self._orgright)/self._size gx = x1 - scaletick*self._width gy = y1 gridrightline = (x1[ne], y1[ne], gx[ne], gy[ne]) if not gridSetn.hide: # draw the grid pen = gridSetn.makeQPen(painter) painter.setPen(pen) utils.plotLinesToPainter(painter, *gridbotline) utils.plotLinesToPainter(painter, *gridleftline) utils.plotLinesToPainter(painter, *gridrightline) # calculate deltas for ticks bdelta = ldelta = rdelta = 0 if not tickSetn.hide: # draw ticks themselves pen = tickSetn.makeQPen(painter) pen.setCapStyle(qt4.Qt.FlatCap) painter.setPen(pen) utils.plotLinesToPainter(painter, *tickbotline) utils.plotLinesToPainter(painter, *tickleftline) utils.plotLinesToPainter(painter, *tickrightline) ldelta += tl*sin30 bdelta += tl*cos30 rdelta += tl if tickLabelSetn is not None and not tickLabelSetn.hide: # compute the labels for the ticks tleftlabels = self._getLabels(tickleft*mv, '%Vg') trightlabels = self._getLabels(tickright*mv, '%Vg') tbotlabels = self._getLabels(tickbot*mv, '%Vg') painter.setPen( tickLabelSetn.makeQPen() ) font = tickLabelSetn.makeQFont(painter) painter.setFont(font) fm = utils.FontMetrics(font, painter.device()) sp = fm.leading() + fm.descent() off = tickLabelSetn.get('offset').convert(painter) # draw tick labels in each direction hlabbot = wlableft = wlabright = 0 for l, x, y in izip(tbotlabels, tickbotline[2], tickbotline[3]+off): r = utils.Renderer(painter, font, x, y, l, 0, 1, 0) bounds = r.render() hlabbot = max(hlabbot, bounds[3]-bounds[1]) for l, x, y in izip(tleftlabels, tickleftline[2]-off-sp, tickleftline[3]): r = utils.Renderer(painter, font, x, y, l, 1, 0, 0) bounds = r.render() wlableft = max(wlableft, bounds[2]-bounds[0]) for l, x, y in izip(trightlabels,tickrightline[2]+off+sp, tickrightline[3]): r = utils.Renderer(painter, font, x, y, l, -1, 0, 0) bounds = r.render() wlabright = max(wlabright, bounds[2]-bounds[0]) bdelta += hlabbot+off+sp ldelta += wlableft+off+sp rdelta += wlabright+off+sp if labelSetn is not None and not labelSetn.hide: # draw label on edges (if requested) painter.setPen( labelSetn.makeQPen() ) font = labelSetn.makeQFont(painter) painter.setFont(font) fm = utils.FontMetrics(font, painter.device()) sp = fm.leading() + fm.descent() off = labelSetn.get('offset').convert(painter) # bottom label r = utils.Renderer(painter, font, self._box[0]+self._width/2, self._box[3] + bdelta + off, self.settings.labelbottom, 0, 1) r.render() # left label - rotate frame before drawing so we can get # the bounds correct r = utils.Renderer(painter, font, 0, -sp, self.settings.labelleft, 0, -1) painter.save() painter.translate(self._box[0]+self._width*0.25 - ldelta - off, 0.5*(self._box[1]+self._box[3])) painter.rotate(-60) r.render() painter.restore() # right label r = utils.Renderer(painter, font, 0, -sp, self.settings.labelright, 0, -1) painter.save() painter.translate(self._box[0]+self._width*0.75 + ldelta + off, 0.5*(self._box[1]+self._box[3])) painter.rotate(60) r.render() painter.restore()
def drawErrorBars(self, painter, posns, barwidth, yvals, dataset, axes, widgetposn): """Draw (optional) error bars on bars.""" s = self.settings if s.errorstyle == 'none': return minval, maxval = self.calculateErrorBars(dataset, yvals) if minval is None and maxval is None: return # handle one sided errors if minval is None: minval = yvals if maxval is None: maxval = yvals # convert errors to coordinates ishorz = s.direction == 'horizontal' mincoord = axes[not ishorz].dataToPlotterCoords(widgetposn, minval) mincoord = N.clip(mincoord, -32767, 32767) maxcoord = axes[not ishorz].dataToPlotterCoords(widgetposn, maxval) maxcoord = N.clip(maxcoord, -32767, 32767) # draw error bars painter.setPen( self.settings.ErrorBarLine.makeQPenWHide(painter) ) w = barwidth*0.25 if ishorz: utils.plotLinesToPainter(painter, mincoord, posns, maxcoord, posns) if s.errorstyle == 'barends': utils.plotLinesToPainter(painter, mincoord, posns-w, mincoord, posns+w) utils.plotLinesToPainter(painter, maxcoord, posns-w, maxcoord, posns+w) else: utils.plotLinesToPainter(painter, posns, mincoord, posns, maxcoord) if s.errorstyle == 'barends': utils.plotLinesToPainter(painter, posns-w, mincoord, posns+w, mincoord) utils.plotLinesToPainter(painter, posns-w, maxcoord, posns+w, maxcoord)
def drawErrorBars(self, painter, posns, barwidth, yvals, dataset, axes, widgetposn): """Draw (optional) error bars on bars.""" s = self.settings if s.errorstyle == 'none': return minval, maxval = self.calculateErrorBars(dataset, yvals) if minval is None and maxval is None: return # handle one sided errors if minval is None: minval = yvals if maxval is None: maxval = yvals # convert errors to coordinates ishorz = s.direction == 'horizontal' mincoord = axes[not ishorz].dataToPlotterCoords(widgetposn, minval) mincoord = N.clip(mincoord, -32767, 32767) maxcoord = axes[not ishorz].dataToPlotterCoords(widgetposn, maxval) maxcoord = N.clip(maxcoord, -32767, 32767) # draw error bars painter.setPen(self.settings.ErrorBarLine.makeQPenWHide(painter)) w = barwidth * 0.25 if ishorz: utils.plotLinesToPainter(painter, mincoord, posns, maxcoord, posns) if s.errorstyle == 'barends': utils.plotLinesToPainter(painter, mincoord, posns - w, mincoord, posns + w) utils.plotLinesToPainter(painter, maxcoord, posns - w, maxcoord, posns + w) else: utils.plotLinesToPainter(painter, posns, mincoord, posns, maxcoord) if s.errorstyle == 'barends': utils.plotLinesToPainter(painter, posns - w, mincoord, posns + w, mincoord) utils.plotLinesToPainter(painter, posns - w, maxcoord, posns + w, maxcoord)
def _drawTickSet(self, painter, tickSetn, gridSetn, tickbot, tickleft, tickright, tickLabelSetn=None, labelSetn=None): '''Draw a set of ticks (major or minor). tickSetn: tick setting to get line details gridSetn: setting for grid line (if any) tickXXX: tick arrays for each axis tickLabelSetn: setting used to label ticks, or None if minor ticks labelSetn: setting for labels, if any ''' # this is mostly a lot of annoying trigonometry # compute line ends for ticks and grid lines tl = tickSetn.get('length').convert(painter) mv = self._maxVal() # bottom ticks x1 = (tickbot - self._orgbot)/self._size*self._width + self._box[0] x2 = x1 - tl * sin30 y1 = self._box[3] + N.zeros(x1.shape) y2 = y1 + tl * cos30 tickbotline = (x1, y1, x2, y2) # bottom grid (removing lines at edge of plot) scaletick = 1 - (tickbot-self._orgbot)/self._size gx = x1 + scaletick*self._width*sin30 gy = y1 - scaletick*self._width*cos30 ne = (scaletick > 1e-6) & (scaletick < (1-1e-6)) gridbotline = (x1[ne], y1[ne], gx[ne], gy[ne]) # left ticks x1 = -(tickleft - self._orgleft)/self._size*self._width*sin30 + ( self._box[0] + self._box[2])*0.5 x2 = x1 - tl * sin30 y1 = (tickleft - self._orgleft)/self._size*self._width*cos30 + self._box[1] y2 = y1 - tl * cos30 tickleftline = (x1, y1, x2, y2) # left grid scaletick = 1 - (tickleft-self._orgleft)/self._size gx = x1 + scaletick*self._width*sin30 gy = self._box[3] + N.zeros(y1.shape) ne = (scaletick > 1e-6) & (scaletick < (1-1e-6)) gridleftline = (x1[ne], y1[ne], gx[ne], gy[ne]) # right ticks x1 = -(tickright - self._orgright)/self._size*self._width*sin30+self._box[2] x2 = x1 + tl y1 = -(tickright - self._orgright)/self._size*self._width*cos30+self._box[3] y2 = y1 tickrightline = (x1, y1, x2, y2) # right grid scaletick = 1 - (tickright-self._orgright)/self._size gx = x1 - scaletick*self._width gy = y1 gridrightline = (x1[ne], y1[ne], gx[ne], gy[ne]) if not gridSetn.hide: # draw the grid pen = gridSetn.makeQPen(painter) painter.setPen(pen) utils.plotLinesToPainter(painter, *gridbotline) utils.plotLinesToPainter(painter, *gridleftline) utils.plotLinesToPainter(painter, *gridrightline) # calculate deltas for ticks bdelta = ldelta = rdelta = 0 if not tickSetn.hide: # draw ticks themselves pen = tickSetn.makeQPen(painter) pen.setCapStyle(qt4.Qt.FlatCap) painter.setPen(pen) utils.plotLinesToPainter(painter, *tickbotline) utils.plotLinesToPainter(painter, *tickleftline) utils.plotLinesToPainter(painter, *tickrightline) ldelta += tl*sin30 bdelta += tl*cos30 rdelta += tl if tickLabelSetn is not None and not tickLabelSetn.hide: # compute the labels for the ticks tleftlabels = self._getLabels(tickleft*mv, '%Vg') trightlabels = self._getLabels(tickright*mv, '%Vg') tbotlabels = self._getLabels(tickbot*mv, '%Vg') painter.setPen( tickLabelSetn.makeQPen() ) font = tickLabelSetn.makeQFont(painter) painter.setFont(font) fm = utils.FontMetrics(font, painter.device()) sp = fm.leading() + fm.descent() off = tickLabelSetn.get('offset').convert(painter) # draw tick labels in each direction hlabbot = wlableft = wlabright = 0 for l, x, y in izip(tbotlabels, tickbotline[2], tickbotline[3]+off): r = utils.Renderer(painter, font, x, y, l, 0, 1, 0) bounds = r.render() hlabbot = max(hlabbot, bounds[3]-bounds[1]) for l, x, y in izip(tleftlabels, tickleftline[2]-off-sp, tickleftline[3]): r = utils.Renderer(painter, font, x, y, l, 1, 0, 0) bounds = r.render() wlableft = max(wlableft, bounds[2]-bounds[0]) for l, x, y in izip(trightlabels,tickrightline[2]+off+sp, tickrightline[3]): r = utils.Renderer(painter, font, x, y, l, -1, 0, 0) bounds = r.render() wlabright = max(wlabright, bounds[2]-bounds[0]) bdelta += hlabbot+off+sp ldelta += wlableft+off+sp rdelta += wlabright+off+sp if labelSetn is not None and not labelSetn.hide: # draw label on edges (if requested) painter.setPen( labelSetn.makeQPen() ) font = labelSetn.makeQFont(painter) painter.setFont(font) fm = utils.FontMetrics(font, painter.device()) sp = fm.leading() + fm.descent() off = labelSetn.get('offset').convert(painter) # bottom label r = utils.Renderer(painter, font, self._box[0]+self._width/2, self._box[3] + bdelta + off, self.settings.labelbottom, 0, 1) r.render() # left label - rotate frame before drawing so we can get # the bounds correct r = utils.Renderer(painter, font, 0, -sp, self.settings.labelleft, 0, -1) painter.save() painter.translate(self._box[0]+self._width*0.25 - ldelta - off, 0.5*(self._box[1]+self._box[3])) painter.rotate(-60) r.render() painter.restore() # right label r = utils.Renderer(painter, font, 0, -sp, self.settings.labelright, 0, -1) painter.save() painter.translate(self._box[0]+self._width*0.75 + ldelta + off, 0.5*(self._box[1]+self._box[3])) painter.rotate(60) r.render() painter.restore()
def draw(self, parentposn, phelper, outerbounds=None): """Draw the widget.""" posn = plotters.GenericPlotter.draw(self, parentposn, phelper, outerbounds=outerbounds) x1, y1, x2, y2 = posn s = self.settings d = self.document # hide if hidden! if s.hide: return # get axes widgets axes = self.parent.getAxes((s.xAxis, s.yAxis)) # return if there's no proper axes if (None in axes or axes[0].settings.direction != 'horizontal' or axes[1].settings.direction != 'vertical'): return # ignore non existing datasets try: data1 = d.data[s.data1] data2 = d.data[s.data2] except KeyError: return # require 2d datasets if data1.dimensions != 2 or data2.dimensions != 2: return # clip data within bounds of plotter cliprect = self.clipAxesBounds(axes, posn) painter = phelper.painter(self, posn, clip=cliprect) # get base length (ensure > 0) baselength = max(s.get('baselength').convert(painter), 1e-6) # try to be nice if the datasets don't match data1st, data2nd = data1.data, data2.data xw = min(data1st.shape[1], data2nd.shape[1]) yw = min(data1st.shape[0], data2nd.shape[0]) # construct indices into datasets yvals, xvals = N.mgrid[0:yw, 0:xw] # convert using 1st dataset to axes values xdsvals, ydsvals = data1.indexToPoint(xvals.ravel(), yvals.ravel()) # convert using axes to plotter values xplotter = axes[0].dataToPlotterCoords(posn, xdsvals) yplotter = axes[1].dataToPlotterCoords(posn, ydsvals) pen = s.Line.makeQPenWHide(painter) painter.setPen(pen) if s.mode == 'cartesian': dx = (data1st[:yw, :xw] * baselength).ravel() dy = (data2nd[:yw, :xw] * baselength).ravel() elif s.mode == 'polar': r = data1st[:yw, :xw].ravel() * baselength theta = data2nd[:yw, :xw].ravel() dx = r * N.cos(theta) dy = r * N.sin(theta) x1, x2 = xplotter - dx, xplotter + dx y1, y2 = yplotter + dy, yplotter - dy if s.arrowfront == 'none' and s.arrowback == 'none': utils.plotLinesToPainter(painter, x1, y1, x2, y2, cliprect) else: arrowsize = s.get('arrowsize').convert(painter) painter.setBrush(s.get('Fill').makeQBrushWHide()) # this is backward - have to convert from dx, dy to angle, length angles = 180 - N.arctan2(dy, dx) * (180. / N.pi) lengths = N.sqrt(dx**2 + dy**2) * 2 # scale arrow heads by arrow length if requested if s.scalearrow: arrowsizes = (arrowsize / baselength / 2) * lengths else: arrowsizes = N.zeros(lengths.shape) + arrowsize for x, y, l, a, asize in itertools.izip(x2, y2, lengths, angles, arrowsizes): if l != 0.: utils.plotLineArrow(painter, x, y, l, a, asize, arrowleft=s.arrowfront, arrowright=s.arrowback)
def draw(self, parentposn, phelper, outerbounds = None): """Draw the widget.""" posn = plotters.GenericPlotter.draw(self, parentposn, phelper, outerbounds = outerbounds) x1, y1, x2, y2 = posn s = self.settings d = self.document # hide if hidden! if s.hide: return # get axes widgets axes = self.parent.getAxes( (s.xAxis, s.yAxis) ) # return if there's no proper axes if ( None in axes or axes[0].settings.direction != 'horizontal' or axes[1].settings.direction != 'vertical' ): return # ignore non existing datasets try: data1 = d.data[s.data1] data2 = d.data[s.data2] except KeyError: return # require 2d datasets if data1.dimensions != 2 or data2.dimensions != 2: return # clip data within bounds of plotter cliprect = self.clipAxesBounds(axes, posn) painter = phelper.painter(self, posn, clip=cliprect) baselength = s.get('baselength').convert(painter) # try to be nice if the datasets don't match data1st, data2nd = data1.data, data2.data xw = min(data1st.shape[1], data2nd.shape[1]) yw = min(data1st.shape[0], data2nd.shape[0]) # construct indices into datasets yvals, xvals = N.mgrid[0:yw, 0:xw] # convert using 1st dataset to axes values xdsvals, ydsvals = data1.indexToPoint(xvals.ravel(), yvals.ravel()) # convert using axes to plotter values xplotter = axes[0].dataToPlotterCoords(posn, xdsvals) yplotter = axes[1].dataToPlotterCoords(posn, ydsvals) pen = s.Line.makeQPenWHide(painter) painter.setPen(pen) if s.mode == 'cartesian': dx = (data1st[:yw, :xw] * baselength).ravel() dy = (data2nd[:yw, :xw] * baselength).ravel() elif s.mode == 'polar': r = data1st[:yw, :xw].ravel() * baselength theta = data2nd[:yw, :xw].ravel() dx = r * N.cos(theta) dy = r * N.sin(theta) x1, x2 = xplotter-dx, xplotter+dx y1, y2 = yplotter+dy, yplotter-dy if s.arrowfront == 'none' and s.arrowback == 'none': utils.plotLinesToPainter(painter, x1, y1, x2, y2, cliprect) else: arrowsize = s.get('arrowsize').convert(painter) painter.setBrush( s.get('Fill').makeQBrushWHide() ) # this is backward - have to convert from dx, dy to angle, length angles = 180 - N.arctan2(dy, dx) * (180./N.pi) lengths = N.sqrt(dx**2+dy**2) * 2 # scale arrow heads by arrow length if requested if s.scalearrow: arrowsizes = (arrowsize/baselength/2) * lengths else: arrowsizes = N.zeros(lengths.shape) + arrowsize for x, y, l, a, asize in itertools.izip(x2, y2, lengths, angles, arrowsizes): utils.plotLineArrow(painter, x, y, l, a, asize, arrowleft=s.arrowfront, arrowright=s.arrowback)