Author: croberts
Date: 2011-11-21 20:09:23 +0000 (Mon, 21 Nov 2011)
New Revision: 5142
Added:
branches/noflash/cumin/python/cumin/charts.py
Log:
Got a little whack happy with previous commit. Reverting to include charts.py for now.
Added: branches/noflash/cumin/python/cumin/charts.py
===================================================================
--- branches/noflash/cumin/python/cumin/charts.py (rev 0)
+++ branches/noflash/cumin/python/cumin/charts.py 2011-11-21 20:09:23 UTC (rev 5142)
@@ -0,0 +1,439 @@
+import logging
+
+from cairo import *
+from random import random
+from time import time
+from math import sqrt, pi, ceil
+
+from formats import *
+from util import *
+
+log = logging.getLogger("cumin.chart")
+
+class CuminPieChart(object):
+ def __init__(self, radius=400):
+ self.width = radius * 2
+ self.height = self.width
+ self.centerx = radius
+ self.centery = radius
+ self.radius = radius
+
+ def plot_pie(self, slices, colors):
+ surface = ImageSurface(FORMAT_ARGB32, int(self.width), int(self.height))
+ cr = Context(surface)
+ cr.set_line_width(2)
+
+ total = float(sum(slices))
+
+ if not total:
+ # draw a grey placeholder circle
+ cr.new_path()
+ cr.arc(self.centerx, self.centery,
+ self.radius, 0, 2 * pi)
+ cr.close_path()
+ cr.set_source_rgba(0, 0, 0, 0.1)
+ cr.stroke()
+ return surface
+
+ wedge = cum_wedges = end = 0.0
+
+ # are we going to draw more than one slice
+ draw_border = sum([1 for x in slices if x > 0]) > 1
+
+ for slice, color in zip(slices, colors):
+ if slice > 0:
+ wedge = slice / total
+
+ start = end
+ end = (cum_wedges + wedge) * 2 * pi
+ cum_wedges += wedge
+
+ # draw the wedge
+ self.draw_slice_path(cr, self.centerx, self.centery,
+ self.radius, start, end)
+ cr.set_source_rgb(*color)
+ cr.fill()
+
+ # draw a white border
+ if draw_border:
+ self.draw_slice_path(cr, self.centerx, self.centery,
+ self.radius, start, end)
+ cr.set_source_rgb(1,1,1)
+ cr.stroke()
+
+ return surface
+
+ def draw_slice_path(self, cr, x, y, radius, start, end):
+ cr.new_path()
+ cr.move_to(x, y)
+ cr.arc(x, y, radius, start, end)
+ cr.close_path()
+
+class HeatMapChart(object):
+ def __init__(self, width=400, height=400):
+ self.width = width # final width
+ self.height = height
+ self.max_width = width # max width
+ self.max_height = height
+ self.cols = 0 # columns
+ self.rows = 0
+ self.max_size = 28 # size of each slot
+ self.surface = None
+ self.gap = 3
+
+ def plot_colored_shape(self, interior, shape, width, height):
+ surface = ImageSurface(FORMAT_ARGB32, int(width), int(height))
+ cr = Context(surface)
+ cr.set_line_width(1)
+
+ cr.set_source_rgba(1.0, 1.0, 1.0, 1.0)
+ cr.rectangle(0, 0, width - 1, height - 1)
+ cr.fill()
+
+ cr.set_line_width(2)
+ self._plot_shape(cr, interior, shape, 0, 0, width, height)
+ return surface
+
+ def plot_colored_rect(self, interior, width, height):
+ surface = ImageSurface(FORMAT_ARGB32, int(width), int(height))
+ cr = Context(surface)
+ cr.set_line_width(1)
+
+ cr.set_source_rgb(*interior)
+ cr.rectangle(0, 0, width - 1, height - 1)
+ cr.fill()
+ return surface
+
+ def plot_state(self, state, width, height):
+ surface = ImageSurface(FORMAT_ARGB32, int(width), int(height))
+ cr = Context(surface)
+ cr.set_line_width(2)
+
+ self._plot_square(cr, (0.7, 0.7, 0.7), state, 0, 0, width, height)
+ return surface
+
+ def _plot_square(self, cr, colors, state, x, y, width, height):
+ if state == "Unclaimed":
+ shape = "box"
+ elif state == "Claimed":
+ shape = "solid"
+ elif state in ("Owner", "Unavailable"):
+ shape = "diagonal"
+ elif state in ("Matched", "Preempting",
"Preempting/Matched"):
+ shape = "filled_triangle"
+ else:
+ shape = "box"
+
+ self._plot_shape(cr, colors, shape, x, y, width, height)
+
+ def _plot_shape(self, cr, colors, shape, x, y, width, height):
+ cr.set_source_rgb(*colors)
+ #cr.move_to(x, y)
+ cr.rectangle(x, y, width - self.gap, height - self.gap)
+
+ if shape == "box": # leave empty
+ cr.stroke()
+ elif shape == "solid": # solid fill
+ cr.fill()
+ elif shape == "diagonal": # diagonal line
+ cr.move_to(x, y)
+ cr.line_to(x + width - self.gap, y + height - self.gap)
+ cr.stroke()
+ elif shape == "filled_triangle": # triangle
+ cr.stroke()
+ cr.move_to(x + width - self.gap, y)
+ cr.line_to(x, y + height - self.gap)
+ cr.line_to(x + width - self.gap, y + height - self.gap)
+ cr.close_path()
+ cr.fill()
+
+ def plot_shapes(self, shape_info, zl):
+ count = len(shape_info)
+ slot_size = self.slot_size(count, zl)
+ x = 0
+ y = 0
+ col = 0
+ self.rows = 1
+ # the width and height depend on the number of slots
+ self.surface = ImageSurface(FORMAT_ARGB32, int(self.width), int(self.height))
+ cr = Context(self.surface)
+
+ cr.set_line_width( zl == 1 and 1 or 2 )
+
+ for slot in shape_info:
+ interior = slot["color"]
+ shape = slot["shape"]
+
+ if col >= self.cols:
+ x = 0
+ y = y + slot_size
+ col = 0
+ self.rows = self.rows + 1
+
+ self._plot_shape(cr, interior, shape, x, y, slot_size, slot_size)
+
+ x = x + slot_size
+ col = col + 1
+ return slot_size
+
+ def slot_size(self, count, zoom=1):
+ sq = sqrt(count)
+ cols = int(sq)
+ if sq > cols:
+ cols = cols + 1
+ size = int(self.width * zoom / cols)
+ if size < 2:
+ size = 2
+ if size > self.max_size * zoom:
+ size = self.max_size * zoom
+
+ self.width = int((size * cols) + 1)
+ self.height = int((ceil(count * 1.0 / cols) * size) + 1)
+ self.cols = cols
+ return size
+
+ def write(self, writer):
+ self.surface.write_to_png(writer)
+
+class TimeSeriesChart(object):
+ def __init__(self, width, height, interval=0):
+ self.width = width - 40
+ self.height = height - 20
+ real_height = height + ((interval > 10) and 12 or 0)
+ self.surface = ImageSurface(FORMAT_ARGB32, int(width), int(real_height))
+ self.surface.set_device_offset(1.5, 5.5)
+ self.x_max = 1
+ self.x_min = 0
+ self.y_max = 1
+ self.y_min = 0
+
+ def plot_values(self, samples, color=(0, 0, 0)):
+ cr = Context(self.surface)
+ cr.set_line_width(1.5)
+ cr.set_source_rgba(color[0], color[1], color[2], 0.66)
+
+ tnow = time()
+ points = list()
+
+ for dt, value, dev in samples:
+ if value is None:
+ continue
+
+ t = secs(dt)
+
+ # prevent line from drawing off right side of grid
+ if tnow - t < 0:
+ t = tnow
+
+ x = self.width - ((tnow - t) / float(self.x_max)) * self.width
+ y = self.height - (value / float(self.y_max)) * self.height
+ cr.line_to(x, y)
+ if x < 0:
+ break
+ points.append([int(x), int(y), "%.2f" % value])
+
+ cr.stroke()
+ return points
+
+ def plot_ticks(self, samples, color=(0, 0, 0)):
+ cr = Context(self.surface)
+ cr.set_line_width(1.5)
+
+ tnow = time()
+ dot_size = max(int(self.height / 100), 2)
+ half_dot = int(dot_size / 2)
+
+ for dt, value, dev in samples:
+ if value is None:
+ continue
+
+ t = secs(dt)
+
+ # prevent line from drawing off right side of grid
+ if tnow - t < 0:
+ t = tnow
+
+ cr.set_source_rgba(color[0], color[1], color[2], 0.66)
+ x = self.width - ((tnow - t) / float(self.x_max)) * self.width
+ y = self.height - (value / float(self.y_max)) * self.height
+ cr.move_to(x, y)
+ cr.rectangle(x - half_dot, y - half_dot, dot_size, dot_size)
+ cr.fill()
+
+ if dev:
+ cr.set_source_rgba(0, 0, 0, 0.1)
+ half_dev = int(((float(dev) / float(self.y_max)) * self.height) / 2)
+ cr.move_to(x - dot_size, y - half_dev)
+ cr.line_to(x + dot_size, y - half_dev)
+
+ cr.move_to(x, y - half_dev)
+ cr.line_to(x, y + half_dev)
+
+ cr.move_to(x - dot_size, y + half_dev)
+ cr.line_to(x + dot_size, y + half_dev)
+
+ cr.stroke()
+
+ def plot_legend(self, titles, colors):
+ cr = Context(self.surface)
+ cr.set_line_width(1.5)
+ title_xy = dict()
+
+ for i, item in enumerate(zip(titles, colors)):
+ title, color = item
+ y = 16 + i * 16
+
+ cr.set_source_rgba(1, 1, 1, 0.75)
+ cr.rectangle(4, y - 12, 16 + 6.5 * len(title), 16)
+ cr.fill()
+ cr.stroke()
+
+ cr.set_source_rgb(*color)
+ cr.rectangle(8, y, 8, -8)
+ cr.fill()
+ cr.stroke()
+
+ cr.move_to(20, y)
+ cr.set_source_rgb(0, 0, 0)
+ cr.show_text(title)
+ cr.stroke()
+
+ width, height = cr.text_extents(title)[2:4]
+ title_xy[color] = [width, y]
+ return title_xy
+
+ def plot_frame(self, color=(0.8, 0.8, 0.8)):
+ cr = Context(self.surface)
+ cr.set_line_width(1)
+ cr.set_source_rgb(*color)
+
+ cr.move_to(self.width, 0)
+ cr.line_to(self.width, self.height)
+ cr.line_to(0, self.height)
+
+ cr.stroke()
+
+ def plot_interval(self, interval):
+ if interval > 10:
+ cr = Context(self.surface)
+ cr.set_line_width(0.2)
+ cr.set_source_rgb(0.2, 0.2, 0.2)
+
+ cr.move_to(2, self.height + 24)
+ cr.show_text("* Samples averaged over %i second interval" %
interval)
+ cr.stroke()
+
+ def plot_x_axis(self, intervals, step):
+ cr = Context(self.surface)
+ cr.set_line_width(0.2)
+ cr.set_source_rgb(0.6, 0.6, 0.6)
+
+ interval = self.width / float(intervals)
+ absmax = self.x_max - self.x_min
+
+ for i in range(0, intervals + 1):
+ x = self.width - (i * interval)
+
+ cr.move_to(x, 0)
+ cr.line_to(x, self.height + 12)
+ cr.move_to(x + 2, self.height + 12)
+
+ if i % step == 0:
+ value = absmax - (absmax * x / float(self.width))
+ text = fmt_duration_brief(value)
+ if text:
+ cr.show_text(text)
+
+ cr.stroke()
+
+ def plot_y_axis(self, intervals, step):
+ cr = Context(self.surface)
+ cr.set_line_width(0.2)
+ cr.set_source_rgb(0.6, 0.6, 0.6)
+
+ interval = self.height / float(intervals)
+
+ for i in range(0, intervals + 1):
+ y = self.height - (i * interval)
+
+ cr.move_to(0, y)
+ cr.line_to(self.width + 3, y)
+ cr.move_to(self.width + 4, y + 3)
+
+ if i % step == 0:
+ fraction = (self.height - y) / float(self.height)
+ value = fraction * self.y_max - self.y_min
+ value = int(round(value + self.y_min))
+
+ if value >= 1000000:
+ svalue = "%.2fM" % (round(value / 1000000.0, 2))
+ elif value >= 10000:
+ svalue = "%ik" % int(round(value / 1000.0, -1))
+ else:
+ svalue = str(value)
+
+ cr.show_text(svalue)
+
+ cr.stroke()
+
+ def write(self, writer):
+ self.surface.write_to_png(writer)
+
+class StackedValueChart(TimeSeriesChart):
+ def __init__(self, width, height, legend_height = 0):
+ super(StackedValueChart, self).__init__(width, height)
+
+ if legend_height:
+ self.surface = ImageSurface(FORMAT_ARGB32, int(width), int(height +
legend_height))
+ self.surface.set_device_offset(1.5, 5.5)
+
+ self.legend_height = legend_height
+ self.bar_width = 2
+
+ def plot_values(self, dt, samples, colors):
+ cr = Context(self.surface)
+ cr.set_line_width(self.bar_width)
+
+ tnow = time()
+ y = self.height
+ v = 0
+ for value, color in zip(samples, colors):
+ if value is None:
+ continue
+
+ t = secs(dt)
+
+ if tnow - t < 0:
+ t = tnow
+
+ x = self.width - ((tnow - t) / float(self.x_max)) * self.width
+ cr.move_to(x, y)
+ cr.set_source_rgba(color[0], color[1], color[2], 0.66)
+
+ v = v + value
+ y = self.height - (v / float(self.y_max)) * self.height
+ cr.line_to(x, y)
+ cr.stroke()
+
+ def plot_legend(self, titles, colors):
+ cr = Context(self.surface)
+
+ for i, item in enumerate(zip(titles, colors)):
+ title, color = item
+ y = self.height + 26 + i * 16
+
+ cr.set_source_rgba(1, 1, 1, 0.75)
+ cr.rectangle(4, y - 12, 16 + 6.5 * len(title), 16)
+ cr.fill()
+ cr.stroke()
+
+ cr.set_source_rgb(*color)
+ cr.rectangle(8, y, 8, -8)
+ cr.fill()
+ cr.stroke()
+
+ cr.move_to(20, y)
+ cr.set_source_rgb(0, 0, 0)
+ cr.show_text(title)
+ cr.stroke()
+