1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com)
5 # Jockey is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the
7 # Free Software Foundation; either version 2, or (at your option) any
10 # Jockey is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
26 from pychart_types import *
29 class Entry(chart_object.T):
30 keys = {"line_len" : (UnitType, None,
31 "Length of the sample line for line plots. If omitted, it is set to be theme.default_font_size"),
32 "rect_size" : (UnitType, None,
33 "Size of the sample 'blob' for bar range charts. If omitted, it is set to be 70% of theme.default_size"),
34 "tick_mark": (tick_mark.T, None, ""),
35 "line_style": (line_style.T, None, ""),
36 "fill_style": (fill_style.T, None, ""),
37 "label": (StringType, "???", ""),
39 __doc__ = legend_doc.doc_entry
40 ##AUTOMATICALLY GENERATED
42 ##END AUTOMATICALLY GENERATED
44 def label_width(self):
45 return font.text_width(" " + self.label)
46 def get_line_len(self):
47 return self.line_len or theme.default_font_size
48 def get_rect_size(self):
49 return self.rect_size or theme.default_font_size * 7 / 10.0
51 def sample_width(self):
53 if self.fill_style != None:
54 w += self.get_line_len()
55 elif self.line_style != None:
56 w += self.get_line_len()
57 elif self.tick_mark != None:
58 w += self.tick_mark.size
61 h = font.text_height(self.label)[0]
64 def draw(self, ar, can, x_tick, x_label, y):
65 """Draw a legend entry. X_TICK and X_LABEL are the X location \
66 (in points) of where the sample and label are drawn."""
68 rect_size = self.get_rect_size()
69 line_len = self.get_line_len()
71 nr_lines = len(self.label.split("\n"))
72 text_height = font.text_height(self.label)[0]
73 line_height = text_height / float(nr_lines)
74 y_center = y + text_height - line_height/1.5
76 if self.fill_style != None:
77 can.rectangle(self.line_style, self.fill_style,
78 x_tick, y_center - rect_size/2.0,
80 y_center + rect_size/2.0)
81 elif self.line_style != None:
82 can.line(self.line_style, x_tick, y_center,
83 x_tick + line_len, y_center)
84 if self.tick_mark != None:
85 self.tick_mark.draw(can, x_tick + line_len/2.0, y_center)
86 elif self.tick_mark != None:
87 self.tick_mark.draw(can, x_tick, y_center)
89 can.show(x_label, y, self.label)
91 __doc__ = """Legend is a rectangular box drawn in a chart to describe
92 the meanings of plots. The contents of a legend box is extracted from
93 plots' "label", "line-style", and "tick-mark" attributes.
95 This module exports a single class, legend.T. Legend.T is a part of
96 an area.T object, and is drawn automatically when area.draw() method
99 class T(chart_object.T):
100 __doc__ = legend_doc.doc
102 "inter_row_sep": (UnitType, 0,
103 "Space between each row in the legend."),
104 "inter_col_sep": (UnitType, 0,
105 "Space between each column in the legend."),
106 "frame_line_style": (line_style.T, line_style.default, ""),
107 "frame_fill_style": (fill_style.T, fill_style.white, ""),
108 "top_fudge": (UnitType, 0,
109 "Amount of space above the first line."),
110 "bottom_fudge": (UnitType, 3,
111 "Amount of space below the last line."),
112 "left_fudge": (UnitType, 5,
113 "Amount of space left of the legend."),
114 "right_fudge": (UnitType, 5,
115 "Amount of space right of the legend."),
116 "loc": (CoordType, None,
117 """Bottom-left corner of the legend.
118 The default location of a legend is the bottom-right end of the chart."""),
119 "shadow": (ShadowType, None, pychart_util.shadow_desc),
120 "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."),
123 ##AUTOMATICALLY GENERATED
125 ##END AUTOMATICALLY GENERATED
126 def draw(self, ar, entries, can):
128 x = ar.loc[0] + ar.size[0] * 1.1
134 nr_rows = min(self.nr_rows, len(entries))
135 nr_cols = (len(entries)-1) / nr_rows + 1
138 max_label_width = [0] * nr_cols
139 max_sample_width = [0] * nr_cols
140 heights = [0] * nr_rows
142 for i in range(len(entries)):
144 (col, row) = divmod(i, nr_rows)
145 max_label_width[col] = max(l.label_width(), max_label_width[col])
146 max_sample_width[col] = max(l.sample_width(), max_sample_width[col])
147 heights[row] = max(l.height(), heights[row])
151 y += self.inter_row_sep * (nr_rows - 1)
154 tot_width = self.inter_col_sep * (nr_cols -1)
155 for w in max_label_width:
157 for w in max_sample_width:
160 can.rectangle(self.frame_line_style, self.frame_fill_style,
162 ymin - self.bottom_fudge,
163 x + tot_width + self.right_fudge,
164 ymax + self.top_fudge,
167 for col in range(nr_cols):
170 for row in range(nr_rows):
171 idx = col * nr_rows + row
172 if idx >= len(entries):
174 this_y -= heights[row]
177 this_y -= self.inter_row_sep
179 l.draw(ar, can, this_x, this_x + max_sample_width[col], this_y)
180 x += max_label_width[col] + max_sample_width[col] + self.inter_col_sep