def drawLabels(self, painter, xplotter, yplotter, textvals, markersize): """Draw labels for the points.""" s = self.settings lab = s.get('Label') # work out offset an alignment deltax = markersize * 1.5 * { 'left': -1, 'centre': 0, 'right': 1 }[lab.posnHorz] deltay = markersize * 1.5 * { 'top': -1, 'centre': 0, 'bottom': 1 }[lab.posnVert] alignhorz = {'left': 1, 'centre': 0, 'right': -1}[lab.posnHorz] alignvert = {'top': -1, 'centre': 0, 'bottom': 1}[lab.posnVert] # make font and len textpen = lab.makeQPen() painter.setPen(textpen) font = lab.makeQFont(painter) angle = lab.angle # iterate over each point and plot each label for x, y, t in itertools.izip(xplotter + deltax, yplotter + deltay, textvals): utils.Renderer(painter, font, x, y, t, alignhorz, alignvert, angle).render()
def draw(self, posn, phelper, outerbounds=None): """Draw the text label.""" s = self.settings d = self.document # exit if hidden if s.hide or s.Text.hide: return text = s.get('label').getData(d) xp, yp = self._getPlotterCoords(posn) if xp is None or yp is None: # we can't calculate coordinates return clip = None if s.clip: clip = qt4.QRectF(qt4.QPointF(posn[0], posn[1]), qt4.QPointF(posn[2], posn[3])) painter = phelper.painter(self, posn, clip=clip) textpen = s.get('Text').makeQPen() painter.setPen(textpen) font = s.get('Text').makeQFont(painter) # we should only be able to move non-dataset labels isnotdataset = (not s.get('xPos').isDataset(d) and not s.get('yPos').isDataset(d)) controlgraphitems = [] for index, (x, y, t) in enumerate( itertools.izip(xp, yp, itertools.cycle(text))): # render the text tbounds = utils.Renderer(painter, font, x, y, t, TextLabel.cnvtalignhorz[s.alignHorz], TextLabel.cnvtalignvert[s.alignVert], s.angle).render() # add cgi for adjustable positions if isnotdataset: cgi = controlgraph.ControlMovableBox(self, tbounds, phelper, crosspos=(x, y)) cgi.labelpt = (x, y) cgi.widgetposn = posn cgi.index = index controlgraphitems.append(cgi) phelper.setControlGraph(self, controlgraphitems)
def draw(self, parentposn, phelper, outerbounds = None): """Plot the key on a plotter.""" s = self.settings if s.hide: return painter = phelper.painter(self, parentposn) font = s.get('Text').makeQFont(painter) painter.setFont(font) height = utils.FontMetrics(font, painter.device()).height() margin = s.marginSize * height showtext = not s.Text.hide # maximum width of text required maxwidth = 1 # total number of layout lines required totallines = 0 # reserve space for the title titlewidth, titleheight = 0, 0 if s.title != '': titlefont = qt4.QFont(font) titlefont.setPointSize(max(font.pointSize() * 1.2, font.pointSize() + 2)) titlewidth, titleheight = utils.Renderer(painter, titlefont, 0, 0, s.title).getDimensions() titleheight += 0.5*margin maxwidth = titlewidth entries = [] # iterate over children and find widgets which are suitable for c in self.parent.children: try: num = c.getNumberKeys() except AttributeError: continue if not c.settings.hide: # add an entry for each key entry for each widget for i in xrange(num): lines = 1 if showtext: w, h = utils.Renderer(painter, font, 0, 0, c.getKeyText(i)).getDimensions() maxwidth = max(maxwidth, w) lines = max(1, math.ceil(float(h)/float(height))) totallines += lines entries.append( (c, i, lines) ) # layout the box layout, (numrows, numcols) = self._layout(entries, totallines) # total size of box symbolwidth = s.get('keyLength').convert(painter) totalwidth = ( (maxwidth + height + symbolwidth)*numcols + height*(numcols-1) ) totalheight = numrows * height + titleheight if not s.Border.hide: totalwidth += 2*margin totalheight += margin # work out horizontal position h = s.horzPosn if h == 'left': x = parentposn[0] + height elif h == 'right': x = parentposn[2] - height - totalwidth elif h == 'centre': x = ( parentposn[0] + 0.5*(parentposn[2] - parentposn[0] - totalwidth) ) elif h == 'manual': x = parentposn[0] + (parentposn[2]-parentposn[0])*s.horzManual # work out vertical position v = s.vertPosn if v == 'top': y = parentposn[1] + height elif v == 'bottom': y = parentposn[3] - totalheight - height elif v == 'centre': y = ( parentposn[1] + 0.5*(parentposn[3] - parentposn[1] - totalheight) ) elif v == 'manual': y = ( parentposn[3] - (parentposn[3]-parentposn[1])*s.vertManual - totalheight ) # for controlgraph boxposn = (x, y) boxdims = (totalwidth, totalheight) # draw surrounding box boxpath = qt4.QPainterPath() boxpath.addRect(qt4.QRectF(x, y, totalwidth, totalheight)) if not s.Background.hide: utils.brushExtFillPath(painter, s.Background, boxpath) if not s.Border.hide: painter.strokePath(boxpath, s.get('Border').makeQPen(painter) ) x += margin y += margin*0.5 # center and draw the title if s.title: xpos = x + 0.5*(totalwidth - (0 if s.Border.hide else 2*margin) - titlewidth) utils.Renderer(painter, titlefont, xpos, y, s.title, alignvert=1).render() y += titleheight textpen = s.get('Text').makeQPen() # plot dataset entries for (plotter, num, xp, yp, lines) in layout: xpos = x + xp*(maxwidth+2*height+symbolwidth) ypos = y + yp*height # plot key symbol painter.save() keyoffset = 0 if s.keyAlign == 'centre': keyoffset = (lines-1)*height/2.0 elif s.keyAlign == 'bottom': keyoffset = (lines-1)*height plotter.drawKeySymbol(num, painter, xpos, ypos+keyoffset, symbolwidth, height) painter.restore() # write key text if showtext: painter.setPen(textpen) utils.Renderer(painter, font, xpos + height + symbolwidth, ypos, plotter.getKeyText(num), -1, 1).render() phelper.setControlGraph( self, [ControlKey(self, parentposn, boxposn, boxdims, height)] )
def _drawAxisLabel(self, painter, sign, outerbounds, texttorender): """Draw an axis label on the plot. texttorender is a list which contains text for the axis to render after checking for collisions """ s = self.settings sl = s.Label label = s.get('Label') font = label.makeQFont(painter) painter.setFont(font) fm = utils.FontMetrics(font, painter.device()) al_spacing = fm.leading() + fm.descent() # an extra offset if required self._delta_axis += label.get('offset').convert(painter) text = s.label # avoid adding blank text to plot if not text: return horz = s.direction == 'horizontal' align1 = 1 align2 = {'centre': 0, 'at-minimum': -1, 'at-maximum': 1}[sl.position] if horz: ax, ay = align2, align1 else: ax, ay = align1, align2 reflected = self.coordReflected if reflected: if horz: ay = -ay else: ax = -ax # angle of text (logic is slightly complex) angle = int(sl.rotate) if horz: if not reflected: angle = 360-angle else: angle = angle+270 if reflected: angle = 360-angle angle = angle % 360 if sl.position == 'centre': x = 0.5*(self.coordParr1 + self.coordParr2) elif sl.position == 'at-minimum': x = self.coordParr1 else: x = self.coordParr2 y = self.coordPerp + sign*(self._delta_axis+al_spacing) if not horz: x, y = y, x # make axis label flush with edge of plot if # it's appropriate if outerbounds is not None and sl.atEdge: if abs(s.otherPosition) < 1e-4 and not reflected: if horz: y = outerbounds[3] ay = -ay else: x = outerbounds[0] ax = -ax elif abs(s.otherPosition-1.) < 1e-4 and reflected: if horz: y = outerbounds[1] ay = -ay else: x = outerbounds[2] ax = -ax r = utils.Renderer(painter, font, x, y, text, ax, ay, angle, usefullheight = True) # make sure text is in plot rectangle if outerbounds is not None: r.ensureInBox( minx=outerbounds[0], maxx=outerbounds[2], miny=outerbounds[1], maxy=outerbounds[3] ) texttorender.insert(0, (r, s.get('Label').makeQPen()) )
def _drawTickLabels(self, phelper, painter, coordticks, sign, outerbounds, texttorender): """Draw tick labels on the plot. texttorender is a list which contains text for the axis to render after checking for collisions """ s = self.settings vertical = s.direction == 'vertical' font = s.get('TickLabels').makeQFont(painter) painter.setFont(font) fm = utils.FontMetrics(font, painter.device()) tl_spacing = fm.leading() + fm.descent() # work out font alignment angle = int(s.TickLabels.rotate) if not self.coordReflected and angle != 0: angle = 360-angle if vertical: # limit tick labels to be directly below/besides axis ax, ay = 1, 0 else: ax, ay = 0, 1 if self.coordReflected: ax, ay = -ax, -ay # get information about text scales tl = s.get('TickLabels') scale = tl.scale pen = tl.makeQPen() # an extra offset if required self._delta_axis += tl.get('offset').convert(painter) def generateTickLabels(): """Return plotter position of labels and label text.""" # get format for labels format = s.TickLabels.format if format.lower() == 'auto': format = self.autoformat # generate positions and labels for posn, tickval in izip(coordticks, self.majortickscalc): text = utils.formatNumber(tickval*scale, format, locale=self.document.locale) yield posn, text # position of label perpendicular to axis perpposn = self.coordPerp + sign*(self._delta_axis+tl_spacing) # use generator function to get labels and positions if s.mode == 'labels': ticklabels = self.generateLabelLabels(phelper) else: ticklabels = generateTickLabels() # iterate over each label maxdim = 0 for parlposn, text in ticklabels: # x and y round other way if vertical if vertical: x, y = perpposn, parlposn else: x, y = parlposn, perpposn r = utils.Renderer(painter, font, x, y, text, alignhorz=ax, alignvert=ay, angle=angle) if outerbounds is not None: # make sure ticks are within plot if vertical: r.ensureInBox(miny=outerbounds[1], maxy=outerbounds[3], extraspace=True) else: r.ensureInBox(minx=outerbounds[0], maxx=outerbounds[2], extraspace=True) bnd = r.getBounds() texttorender.append( (r, pen) ) # keep track of maximum extent of label perpendicular to axis if vertical: maxdim = max(maxdim, bnd[2] - bnd[0]) else: maxdim = max(maxdim, bnd[3] - bnd[1]) # keep track of where we are self._delta_axis += 2*tl_spacing + maxdim
def drawAxes(self, painter, bounds, datarange, outerbounds=None): '''Plot axes.''' s = self.settings t = s.Tick if self._maxradius <= 0.: self._maxradius = 1. atick = AxisTicks(0, self._maxradius, t.number, t.number * 4, extendmin=False, extendmax=False) atick.getTicks() majtick = atick.tickvals # draw ticks as circles if not t.hideannuli: painter.setPen(s.Tick.makeQPenWHide(painter)) painter.setBrush(qt4.QBrush()) for tick in majtick[1:]: radius = tick / self._maxradius painter.drawEllipse( qt4.QRectF( qt4.QPointF(self._xc - radius * self._xscale, self._yc - radius * self._yscale), qt4.QPointF(self._xc + radius * self._xscale, self._yc + radius * self._yscale))) # setup axes plot tl = s.TickLabels scale, format = tl.scale, tl.format if format == 'Auto': format = atick.autoformat painter.setPen(tl.makeQPen()) font = tl.makeQFont(painter) # draw radial axis if not s.TickLabels.hideradial: for tick in majtick[1:]: num = utils.formatNumber(tick * scale, format, locale=self.document.locale) x = tick / self._maxradius * self._xscale + self._xc r = utils.Renderer(painter, font, x, self._yc, num, alignhorz=-1, alignvert=-1, usefullheight=True) r.render() if s.units == 'degrees': angles = [ u'0°', u'30°', u'60°', u'90°', u'120°', u'150°', u'180°', u'210°', u'240°', u'270°', u'300°', u'330°' ] else: angles = [ '0', u'π/6', u'π/3', u'π/2', u'2π/3', u'5π/6', u'π', u'7π/6', u'4π/3', u'3π/2', u'5π/3', u'11π/6' ] align = [(-1, 1), (-1, 1), (-1, 1), (0, 1), (1, 1), (1, 1), (1, 0), (1, -1), (1, -1), (0, -1), (-1, -1), (-1, -1)] if s.direction == 'anticlockwise': angles = angles[0:1] + angles[1:][::-1] # rotate labels if zero not at right if s.position0 == 'top': angles = angles[3:] + angles[:4] elif s.position0 == 'left': angles = angles[6:] + angles[:7] elif s.position0 == 'bottom': angles = angles[9:] + angles[:10] # draw labels around plot if not s.TickLabels.hidetangential: for i in xrange(12): angle = 2 * N.pi / 12 x = self._xc + N.cos(angle * i) * self._xscale y = self._yc + N.sin(angle * i) * self._yscale r = utils.Renderer(painter, font, x, y, angles[i], alignhorz=align[i][0], alignvert=align[i][1], usefullheight=True) r.render() # draw spokes if not t.hidespokes: painter.setPen(s.Tick.makeQPenWHide(painter)) painter.setBrush(qt4.QBrush()) angle = 2 * N.pi / 12 lines = [] for i in xrange(12): x = self._xc + N.cos(angle * i) * self._xscale y = self._yc + N.sin(angle * i) * self._yscale lines.append( qt4.QLineF(qt4.QPointF(self._xc, self._yc), qt4.QPointF(x, y))) painter.drawLines(lines)
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()