def drawBar(bar): stroke_width = self.options.stroke.width ux, uy = cx.device_to_user_distance(stroke_width, stroke_width) if ux < uy: ux = uy cx.set_line_width(ux) # gather bar proportions x = self.layout.chart.x + self.layout.chart.w * bar.x y = self.layout.chart.y + self.layout.chart.h * bar.y w = self.layout.chart.w * bar.w h = self.layout.chart.h * bar.h if (w < 1 or h < 1) and self.options.yvals.skipSmallValues: return # don't draw when the bar is too small if self.options.stroke.shadow: cx.set_source_rgba(0, 0, 0, 0.15) rectangle = self._getShadowRectangle(x, y, w, h) cx.rectangle(*rectangle) cx.fill() if self.options.shouldFill or (not self.options.stroke.hide): if self.options.shouldFill: cx.set_source_rgb(*self.colorScheme[bar.name]) cx.rectangle(x, y, w, h) cx.fill() if not self.options.stroke.hide: cx.set_source_rgb(*hex2rgb(self.options.stroke.color)) cx.rectangle(x, y, w, h) cx.stroke() if bar.yerr: self._renderError(cx, x, y, w, h, bar.yval, bar.yerr) # render yvals above/beside bars if self.options.yvals.show: cx.save() cx.set_font_size(self.options.yvals.fontSize) cx.set_source_rgb(*hex2rgb(self.options.yvals.fontColor)) if callable(self.options.yvals.renderer): label = safe_unicode(self.options.yvals.renderer(bar), self.options.encoding) else: label = safe_unicode(bar.yval, self.options.encoding) extents = cx.text_extents(label) labelW = extents[2] labelH = extents[3] self._renderYVal(cx, label, labelW, labelH, x, y, w, h) cx.restore()
def drawBar(bar): stroke_width = self.options.stroke.width ux, uy = cx.device_to_user_distance(stroke_width, stroke_width) if ux < uy: ux = uy cx.set_line_width(ux) # gather bar proportions x = self.layout.chart.x + self.layout.chart.w * bar.x y = self.layout.chart.y + self.layout.chart.h * bar.y w = self.layout.chart.w * bar.w h = self.layout.chart.h * bar.h if (w < 1 or h < 1) and self.options.yvals.skipSmallValues: return # don't draw when the bar is too small if self.options.stroke.shadow: cx.set_source_rgba(0, 0, 0, 0.15) rectangle = self._getShadowRectangle(x, y, w, h) cx.rectangle(*rectangle) cx.fill() if self.options.shouldFill or (not self.options.stroke.hide): if self.options.shouldFill: cx.set_source_rgb(*self.colorScheme[bar.name]) cx.rectangle(x, y, w, h) cx.fill() if not self.options.stroke.hide: cx.set_source_rgb(*hex2rgb(self.options.stroke.color)) cx.rectangle(x, y, w, h) cx.stroke() if bar.yerr: self._renderError(cx, x, y, w, h, bar.yval, bar.yerr) # render yvals above/beside bars if self.options.yvals.show: cx.save() cx.set_font_size(self.options.yvals.fontSize) cx.set_source_rgb(*hex2rgb(self.options.yvals.fontColor)) if isinstance(self.options.yvals.renderer, collections.Callable): label = safe_unicode(self.options.yvals.renderer(bar), self.options.encoding) else: label = safe_unicode(bar.yval, self.options.encoding) extents = cx.text_extents(label) labelW = extents[2] labelH = extents[3] self._renderYVal(cx, label, labelW, labelH, x, y, w, h) cx.restore()
def _getAxisTickLabelsSize(self, cx, options, axis, ticks): cx.save() cx.select_font_face(options.axis.tickFont, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) cx.set_font_size(options.axis.tickFontSize) max_width = max_height = 0.0 if not axis.hide: extents = [cx.text_extents(safe_unicode( tick[1], options.encoding, ))[2:4] # get width and height as a tuple for tick in ticks] if extents: widths, heights = zip(*extents) max_width, max_height = max(widths), max(heights) if axis.rotate: radians = math.radians(axis.rotate) sin = math.sin(radians) cos = math.cos(radians) max_width, max_height = ( max_width * cos + max_height * sin, max_width * sin + max_height * cos, ) cx.restore() return max_width, max_height
def preparePath(storeName): cx.new_path() firstPoint = True lastX = None if self.options.shouldFill: # Go to the (0,0) coordinate to start drawing the area # cx.move_to(self.layout.chart.x, # self.layout.chart.y + self.layout.chart.h) offset = (1.0 - self.origin) * self.layout.chart.h cx.move_to(self.layout.chart.x, self.layout.chart.y + offset) for point in self.points: if point.name == storeName: if not self.options.shouldFill and firstPoint: # starts the first point of the line cx.move_to( point.x * self.layout.chart.w + self.layout.chart.x, point.y * self.layout.chart.h + self.layout.chart.y, ) firstPoint = False continue cx.line_to( point.x * self.layout.chart.w + self.layout.chart.x, point.y * self.layout.chart.h + self.layout.chart.y, ) # # cx.show_text(str(point.yval)) # we remember the last X coordinate to close the area # properly. See bug #4 lastX = point.x if self.options.shouldFill: # Close the path to the start point y = (1.0 - self.origin) * self.layout.chart.h + self.layout.chart.y cx.line_to(lastX * self.layout.chart.w + self.layout.chart.x, y) cx.line_to(self.layout.chart.x, y) cx.close_path() else: cx.set_source_rgb(*self.colorScheme[storeName]) cx.stroke() # Draw yvals text, add by Zhang Kun if self.options.yvals and self.options.yvals.show: cx.save() cx.set_font_size(self.options.yvals.fontSize) cx.set_source_rgb(*hex2rgb(self.options.yvals.fontColor)) for point in self.points: if point.name == storeName: val_str = safe_unicode(str(point.yval), self.options.encoding) extents = cx.text_extents(val_str) cx.move_to( point.x * self.layout.chart.w + self.layout.chart.x, point.y * self.layout.chart.h + self.layout.chart.y + extents[1] / 2, ) cx.show_text(str(point.yval)) cx.restore()
def get_text_extents(cx, text, font, font_size, encoding): if text: cx.save() cx.select_font_face(font, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) cx.set_font_size(font_size) safe_text = safe_unicode(text, encoding) extents = cx.text_extents(safe_text) cx.restore() return extents[2:4] return (0.0, 0.0)
def _renderXTick(self, cx, i, fontAscent, center): tick = self.xticks[i] if callable(tick): return count = len(self.xticks) cx.select_font_face(self.options.axis.tickFont, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) cx.set_font_size(self.options.axis.tickFontSize) label = safe_unicode(tick[1], self.options.encoding) extents = cx.text_extents(label) labelWidth = extents[2] labelHeight = extents[3] x, y = center cx.move_to(x, y) if self.options.axis.x.rotate: radians = math.radians(self.options.axis.x.rotate) cx.move_to(x - (labelHeight * math.cos(radians)), y + self.options.axis.tickSize + (labelHeight * math.cos(radians)) + 4.0) cx.rotate(radians) cx.show_text(label) cx.rotate(-radians) else: offset1 = i * 2 * math.pi / count offset = math.pi / 2 - offset1 rad = self.layout.chart.h / 2 + 10 x = center[0] - math.cos(offset) * rad y = center[1] - math.sin(offset) * rad cx.move_to(x, y) cx.rotate(offset - math.pi / 2) if math.sin(offset) < 0.0: cx.rotate(math.pi) cx.rel_move_to(0.0, 5.0) cx.rel_move_to(-labelWidth / 2.0, 0) cx.show_text(label) if math.sin(offset) < 0.0: cx.rotate(-math.pi) cx.rotate(-(offset - math.pi / 2)) return label
def _renderYTick(self, cx, tick, center): """Aux method for _renderAxis""" i = tick tick = self.yticks[i] count = len(self.yticks) if callable(tick): return x = center[0] y = center[1] - i * (self.layout.chart.h / 2) / count cx.new_path() cx.move_to(x, y) cx.line_to(x - self.options.axis.tickSize, y) cx.close_path() cx.stroke() cx.select_font_face(self.options.axis.tickFont, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) cx.set_font_size(self.options.axis.tickFontSize) label = safe_unicode(tick[1], self.options.encoding) extents = cx.text_extents(label) labelWidth = extents[2] labelHeight = extents[3] if self.options.axis.y.rotate: radians = math.radians(self.options.axis.y.rotate) cx.move_to(x - self.options.axis.tickSize - (labelWidth * math.cos(radians)) - 4, y + (labelWidth * math.sin(radians)) + labelHeight / (2.0 / math.cos(radians))) cx.rotate(-radians) cx.show_text(label) cx.rotate(radians) # this is probably faster than a save/restore else: cx.move_to(x - self.options.axis.tickSize - labelWidth - 4, y + labelHeight / 2.0) cx.rel_move_to(0.0, -labelHeight / 2.0) cx.show_text(label) return label
def _renderTick(self, cx, tick, x, y, x2, y2, rotate, text_position): """Aux method for _renderXTick and _renderYTick""" if callable(tick): return cx.new_path() cx.move_to(x, y) cx.line_to(x2, y2) cx.close_path() cx.stroke() cx.select_font_face(self.options.axis.tickFont, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) cx.set_font_size(self.options.axis.tickFontSize) label = safe_unicode(tick[1], self.options.encoding) xb, yb, width, height, xa, ya = cx.text_extents(label) x, y = text_position if rotate: cx.save() cx.translate(x, y) cx.rotate(math.radians(rotate)) x = -width / 2.0 y = -height / 2.0 cx.move_to(x - xb, y - yb) cx.show_text(label) if self.debug: cx.rectangle(x, y, width, height) cx.stroke() cx.restore() else: x -= width / 2.0 y -= height / 2.0 cx.move_to(x - xb, y - yb) cx.show_text(label) if self.debug: cx.rectangle(x, y, width, height) cx.stroke() return label
def _renderTitle(self, cx): if self.options.title: cx.save() cx.select_font_face(self.options.titleFont, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) cx.set_font_size(self.options.titleFontSize) cx.set_source_rgb(*hex2rgb(self.options.titleColor)) title = safe_unicode(self.options.title, self.options.encoding) extents = cx.text_extents(title) title_width = extents[2] x = (self.layout.title.x + self.layout.title.w / 2.0 - title_width / 2.0) y = self.layout.title.y - extents[1] cx.move_to(x, y) cx.show_text(title) cx.restore()
def _renderXAxisLabel(self, cx, label_text): label = safe_unicode(label_text, self.options.encoding) x = self.layout.x_label.x + self.layout.x_label.w / 2.0 y = self.layout.x_label.y self._renderAxisLabel(cx, label, x, y, False)
def _renderLegend(self, cx): """This function adds a legend to the chart""" if self.options.legend.hide: return surface_width, surface_height = self.getSurfaceSize() # Compute legend dimensions padding = 4 bullet = 15 width = 0 height = padding keys = self._getDatasetsKeys() cx.select_font_face(self.options.legend.legendFont, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) cx.set_font_size(self.options.legend.legendFontSize) for key in keys: key = safe_unicode(key, self.options.encoding) extents = cx.text_extents(key) width = max(extents[2], width) height += max(extents[3], bullet) + padding width = padding + bullet + padding + width + padding # Compute legend position legend = self.options.legend if legend.position.right is not None: legend.position.left = (surface_width - legend.position.right - width) if legend.position.bottom is not None: legend.position.top = (surface_height - legend.position.bottom - height) # Draw the legend cx.save() cx.rectangle(self.options.legend.position.left, self.options.legend.position.top, width, height) cx.set_source_rgba(1, 1, 1, self.options.legend.opacity) cx.fill_preserve() cx.set_line_width(self.options.legend.borderWidth) cx.set_source_rgb(*hex2rgb(self.options.legend.borderColor)) cx.stroke() def drawKey(key, x, y, text_height): cx.rectangle(x, y, bullet, bullet) cx.set_source_rgb(*self.colorScheme[key]) cx.fill_preserve() cx.set_source_rgb(0, 0, 0) cx.stroke() cx.move_to(x + bullet + padding, y + bullet / 2.0 + text_height / 2.0) cx.show_text(key) cx.set_line_width(1) x = self.options.legend.position.left + padding y = self.options.legend.position.top + padding for key in keys: extents = cx.text_extents(key) drawKey(key, x, y, extents[3]) y += max(extents[3], bullet) + padding cx.restore()
def _renderYAxisLabel(self, cx, label_text): label = safe_unicode(label_text, self.options.encoding) x = self.layout.y_label.x y = self.layout.y_label.y + self.layout.y_label.h / 2.0 self._renderAxisLabel(cx, label, x, y, True)
def preparePath(storeName): cx.new_path() firstPoint = True lastX = None if self.options.shouldFill: # Go to the (0,0) coordinate to start drawing the area #cx.move_to(self.layout.chart.x, # self.layout.chart.y + self.layout.chart.h) offset = (1.0 - self.origin) * self.layout.chart.h cx.move_to(self.layout.chart.x, self.layout.chart.y + offset) for point in self.points: if point.name == storeName: if not self.options.shouldFill and firstPoint: # starts the first point of the line cx.move_to( point.x * self.layout.chart.w + self.layout.chart.x, point.y * self.layout.chart.h + self.layout.chart.y) firstPoint = False continue cx.line_to( point.x * self.layout.chart.w + self.layout.chart.x, point.y * self.layout.chart.h + self.layout.chart.y) # # cx.show_text(str(point.yval)) # we remember the last X coordinate to close the area # properly. See bug #4 lastX = point.x if self.options.shouldFill: # Close the path to the start point y = ((1.0 - self.origin) * self.layout.chart.h + self.layout.chart.y) cx.line_to(lastX * self.layout.chart.w + self.layout.chart.x, y) cx.line_to(self.layout.chart.x, y) cx.close_path() else: cx.set_source_rgb(*self.colorScheme[storeName]) cx.stroke() # Draw yvals text, add by Zhang Kun if self.options.yvals and self.options.yvals.show: cx.save() cx.set_font_size(self.options.yvals.fontSize) cx.set_source_rgb(*hex2rgb(self.options.yvals.fontColor)) for point in self.points: if point.name == storeName: val_str = safe_unicode(str(point.yval), self.options.encoding) extents = cx.text_extents(val_str) cx.move_to( point.x * self.layout.chart.w + self.layout.chart.x, point.y * self.layout.chart.h + self.layout.chart.y + extents[1] / 2) cx.show_text(str(point.yval)) cx.restore()