183 lines
6.7 KiB
Python
183 lines
6.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com)
|
|
#
|
|
# Jockey is free software; you can redistribute it and/or modify it
|
|
# under the terms of the GNU General Public License as published by the
|
|
# Free Software Foundation; either version 2, or (at your option) any
|
|
# later version.
|
|
#
|
|
# Jockey is distributed in the hope that it will be useful, but WITHOUT
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
# for more details.
|
|
#
|
|
import tick_mark
|
|
import font
|
|
import line_style
|
|
import color
|
|
import fill_style
|
|
import chart_object
|
|
import pychart_util
|
|
import types
|
|
import legend_doc
|
|
import theme
|
|
|
|
from pychart_types import *
|
|
from types import *
|
|
|
|
class Entry(chart_object.T):
|
|
keys = {"line_len" : (UnitType, None,
|
|
"Length of the sample line for line plots. If omitted, it is set to be theme.default_font_size"),
|
|
"rect_size" : (UnitType, None,
|
|
"Size of the sample 'blob' for bar range charts. If omitted, it is set to be 70% of theme.default_size"),
|
|
"tick_mark": (tick_mark.T, None, ""),
|
|
"line_style": (line_style.T, None, ""),
|
|
"fill_style": (fill_style.T, None, ""),
|
|
"label": (StringType, "???", ""),
|
|
}
|
|
__doc__ = legend_doc.doc_entry
|
|
##AUTOMATICALLY GENERATED
|
|
|
|
##END AUTOMATICALLY GENERATED
|
|
|
|
def label_width(self):
|
|
return font.text_width(" " + self.label)
|
|
def get_line_len(self):
|
|
return self.line_len or theme.default_font_size
|
|
def get_rect_size(self):
|
|
return self.rect_size or theme.default_font_size * 7 / 10.0
|
|
|
|
def sample_width(self):
|
|
w = 0
|
|
if self.fill_style != None:
|
|
w += self.get_line_len()
|
|
elif self.line_style != None:
|
|
w += self.get_line_len()
|
|
elif self.tick_mark != None:
|
|
w += self.tick_mark.size
|
|
return w
|
|
def height(self):
|
|
h = font.text_height(self.label)[0]
|
|
return h
|
|
|
|
def draw(self, ar, can, x_tick, x_label, y):
|
|
"""Draw a legend entry. X_TICK and X_LABEL are the X location \
|
|
(in points) of where the sample and label are drawn."""
|
|
|
|
rect_size = self.get_rect_size()
|
|
line_len = self.get_line_len()
|
|
|
|
nr_lines = len(self.label.split("\n"))
|
|
text_height = font.text_height(self.label)[0]
|
|
line_height = text_height / float(nr_lines)
|
|
y_center = y + text_height - line_height/1.5
|
|
|
|
if self.fill_style != None:
|
|
can.rectangle(self.line_style, self.fill_style,
|
|
x_tick, y_center - rect_size/2.0,
|
|
x_tick + rect_size,
|
|
y_center + rect_size/2.0)
|
|
elif self.line_style != None:
|
|
can.line(self.line_style, x_tick, y_center,
|
|
x_tick + line_len, y_center)
|
|
if self.tick_mark != None:
|
|
self.tick_mark.draw(can, x_tick + line_len/2.0, y_center)
|
|
elif self.tick_mark != None:
|
|
self.tick_mark.draw(can, x_tick, y_center)
|
|
|
|
can.show(x_label, y, self.label)
|
|
|
|
__doc__ = """Legend is a rectangular box drawn in a chart to describe
|
|
the meanings of plots. The contents of a legend box is extracted from
|
|
plots' "label", "line-style", and "tick-mark" attributes.
|
|
|
|
This module exports a single class, legend.T. Legend.T is a part of
|
|
an area.T object, and is drawn automatically when area.draw() method
|
|
is called. """
|
|
|
|
class T(chart_object.T):
|
|
__doc__ = legend_doc.doc
|
|
keys = {
|
|
"inter_row_sep": (UnitType, 0,
|
|
"Space between each row in the legend."),
|
|
"inter_col_sep": (UnitType, 0,
|
|
"Space between each column in the legend."),
|
|
"frame_line_style": (line_style.T, line_style.default, ""),
|
|
"frame_fill_style": (fill_style.T, fill_style.white, ""),
|
|
"top_fudge": (UnitType, 0,
|
|
"Amount of space above the first line."),
|
|
"bottom_fudge": (UnitType, 3,
|
|
"Amount of space below the last line."),
|
|
"left_fudge": (UnitType, 5,
|
|
"Amount of space left of the legend."),
|
|
"right_fudge": (UnitType, 5,
|
|
"Amount of space right of the legend."),
|
|
"loc": (CoordType, None,
|
|
"""Bottom-left corner of the legend.
|
|
The default location of a legend is the bottom-right end of the chart."""),
|
|
"shadow": (ShadowType, None, pychart_util.shadow_desc),
|
|
"nr_rows": (IntType, 9999, "Number of rows in the legend. If the number of plots in a chart is larger than nr_rows, multiple columns are created in the legend."),
|
|
|
|
}
|
|
##AUTOMATICALLY GENERATED
|
|
|
|
##END AUTOMATICALLY GENERATED
|
|
def draw(self, ar, entries, can):
|
|
if not self.loc:
|
|
x = ar.loc[0] + ar.size[0] * 1.1
|
|
y = ar.loc[1]
|
|
else:
|
|
x = self.loc[0]
|
|
y = self.loc[1]
|
|
|
|
nr_rows = min(self.nr_rows, len(entries))
|
|
nr_cols = (len(entries)-1) / nr_rows + 1
|
|
|
|
ymin = y
|
|
max_label_width = [0] * nr_cols
|
|
max_sample_width = [0] * nr_cols
|
|
heights = [0] * nr_rows
|
|
|
|
for i in range(len(entries)):
|
|
l = entries[i]
|
|
(col, row) = divmod(i, nr_rows)
|
|
max_label_width[col] = max(l.label_width(), max_label_width[col])
|
|
max_sample_width[col] = max(l.sample_width(), max_sample_width[col])
|
|
heights[row] = max(l.height(), heights[row])
|
|
|
|
for h in heights:
|
|
y += h
|
|
y += self.inter_row_sep * (nr_rows - 1)
|
|
ymax = y
|
|
|
|
tot_width = self.inter_col_sep * (nr_cols -1)
|
|
for w in max_label_width:
|
|
tot_width += w
|
|
for w in max_sample_width:
|
|
tot_width += w
|
|
|
|
can.rectangle(self.frame_line_style, self.frame_fill_style,
|
|
x - self.left_fudge,
|
|
ymin - self.bottom_fudge,
|
|
x + tot_width + self.right_fudge,
|
|
ymax + self.top_fudge,
|
|
self.shadow)
|
|
|
|
for col in range(nr_cols):
|
|
this_y = y
|
|
this_x = x
|
|
for row in range(nr_rows):
|
|
idx = col * nr_rows + row
|
|
if idx >= len(entries):
|
|
continue
|
|
this_y -= heights[row]
|
|
l = entries[idx]
|
|
if row != 0:
|
|
this_y -= self.inter_row_sep
|
|
|
|
l.draw(ar, can, this_x, this_x + max_sample_width[col], this_y)
|
|
x += max_label_width[col] + max_sample_width[col] + self.inter_col_sep
|
|
|
|
|