Browse Source

Major and minor x-axis labels

Adds options to
- specify major labels manually
- specify major labels automatically based on an interval
- specify major labels automatically based on a count
- suppress diplay of minor labels
- give major labels a different font size
The 'x_labels_num_limit' option has been removed. This functionality can also be achieved with the new options and now works for other graph types than Line.
pull/26/head
Arjen Stolk 12 years ago
parent
commit
77a55bb10a
  1. 21
      pygal/config.py
  2. 5
      pygal/css/base.css
  3. 22
      pygal/graph/graph.py
  4. 13
      pygal/graph/line.py
  5. 24
      pygal/graph/radar.py

21
pygal/config.py

@ -140,10 +140,21 @@ class Config(object):
"Leave it to None to disable x labels display.", "Leave it to None to disable x labels display.",
str) str)
x_labels_num_limit = Key(None, int, "Label", x_labels_major = Key(
"Limits the number of X labels, defaults to None", None, list, "Label",
"If none, there will be as many x labels as provided in x_labels. When provided - limits the number of X labels" "X labels that will be marked major.",
) str)
x_labels_major_every = Key(
None, int, "Label",
"Mark every n-th x label as major.")
x_labels_major_count = Key(
None, int, "Label",
"Mark n evenly distributed labels as major.")
show_minor_x_labels = Key(
True, bool, "Label", "Set to false to hide x-labels not marked major")
y_labels = Key( y_labels = Key(
None, list, "Label", None, list, "Label",
@ -198,6 +209,8 @@ class Config(object):
"No data", str, "Text", "Text to display when no data is given") "No data", str, "Text", "Text to display when no data is given")
label_font_size = Key(10, int, "Text", "Label font size") label_font_size = Key(10, int, "Text", "Label font size")
major_label_font_size = Key(10, int, "Text", "Major label font size")
value_font_size = Key(8, int, "Text", "Value font size") value_font_size = Key(8, int, "Text", "Value font size")

5
pygal/css/base.css

@ -37,6 +37,11 @@
font-size: {{ font_sizes.label }}; font-size: {{ font_sizes.label }};
} }
.axis text.major {
font-family: monospace;
font-size: {{ font_sizes.major_label }};
}
.series text { .series text {
font-family: monospace; font-family: monospace;
font-size: {{ font_sizes.value }}; font-size: {{ font_sizes.value }};

22
pygal/graph/graph.py

@ -131,8 +131,25 @@ class Graph(BaseGraph):
d='M%f %f v%f' % (0, 0, self.view.height), d='M%f %f v%f' % (0, 0, self.view.height),
class_='line') class_='line')
lastlabel = self._x_labels[-1][0] lastlabel = self._x_labels[-1][0]
if self.x_labels_major:
x_labels_major = self.x_labels_major
elif self.x_labels_major_every:
x_labels_major = [self._x_labels[i][0] for i in xrange(
0, len(self._x_labels), self.x_labels_major_every)]
elif self.x_labels_major_count:
label_count = len(self._x_labels)
major_count = self.x_labels_major_count
if (major_count >= label_count):
x_labels_major = [label[0] for label in self._x_labels]
else:
x_labels_major = [self._x_labels[
int(i * (label_count - 1) / (major_count - 1))][0]
for i in xrange(major_count)]
else:
x_labels_major = []
for label, position in self._x_labels: for label, position in self._x_labels:
major = is_major(position) major = label in x_labels_major
if not (self.show_minor_x_labels or major): continue
guides = self.svg.node(axis, class_='guides') guides = self.svg.node(axis, class_='guides')
x = self.view.x(position) x = self.view.x(position)
y = self.view.height + 5 y = self.view.height + 5
@ -162,7 +179,8 @@ class Graph(BaseGraph):
secondary_ax = self.svg.node( secondary_ax = self.svg.node(
self.nodes['plot'], class_="axis x x2") self.nodes['plot'], class_="axis x x2")
for label, position in self._x_2nd_labels: for label, position in self._x_2nd_labels:
major = is_major(position) major = label in x_labels_major
if not (self.show_minor_x_labels or major): continue
# it is needed, to have the same structure as primary axis # it is needed, to have the same structure as primary axis
guides = self.svg.node(secondary_ax, class_='guides') guides = self.svg.node(secondary_ax, class_='guides')
x = self.view.x(position) x = self.view.x(position)

13
pygal/graph/line.py

@ -87,10 +87,7 @@ class Line(Graph):
self.svg.node(dots, 'circle', cx=x, cy=y, r=self.dots_size, self.svg.node(dots, 'circle', cx=x, cy=y, r=self.dots_size,
class_='dot reactive tooltip-trigger') class_='dot reactive tooltip-trigger')
self._tooltip_data( self._tooltip_data(
dots, "%s: %s" % ( dots, val, x, y)
self.x_labels[i], val)
if self.x_labels and self.x_labels_num_limit
else val, x, y)
self._static_value( self._static_value(
serie_node, val, serie_node, val,
x + self.value_font_size, x + self.value_font_size,
@ -114,13 +111,7 @@ class Line(Graph):
self._points(x_pos) self._points(x_pos)
if self.x_labels: if self.x_labels:
x_labels = zip(self.x_labels, x_pos) self._x_labels = zip(self.x_labels, x_pos)
if (self.x_labels_num_limit and
len(x_labels) > self.x_labels_num_limit):
step = (len(x_labels) - 1) / (self.x_labels_num_limit - 1)
x_labels = [x_labels[int(i * step)]
for i in range(self.x_labels_num_limit)]
self._x_labels = x_labels
else: else:
self._x_labels = None self._x_labels = None

24
pygal/graph/radar.py

@ -72,19 +72,39 @@ class Radar(Line):
format_ = lambda x: '%f %f' % x format_ = lambda x: '%f %f' % x
center = self.view((0, 0)) center = self.view((0, 0))
r = self._rmax r = self._rmax
if self.x_labels_major:
x_labels_major = self.x_labels_major
elif self.x_labels_major_every:
x_labels_major = [self._x_labels[i][0] for i in xrange(
0, len(self._x_labels), self.x_labels_major_every)]
elif self.x_labels_major_count:
label_count = len(self._x_labels)
major_count = self.x_labels_major_count
if (major_count >= label_count):
x_labels_major = [label[0] for label in self._x_labels]
else:
x_labels_major = [self._x_labels[
int(i * label_count / major_count)][0]
for i in xrange(major_count)]
else:
x_labels_major = []
for label, theta in self._x_labels: for label, theta in self._x_labels:
major = label in x_labels_major
if not (self.show_minor_x_labels or major): continue
guides = self.svg.node(axis, class_='guides') guides = self.svg.node(axis, class_='guides')
end = self.view((r, theta)) end = self.view((r, theta))
self.svg.node( self.svg.node(
guides, 'path', guides, 'path',
d='M%s L%s' % (format_(center), format_(end)), d='M%s L%s' % (format_(center), format_(end)),
class_='line') class_='%sline'%('major ' if major else ''))
r_txt = (1 - self._box.__class__.margin) * self._box.ymax r_txt = (1 - self._box.__class__.margin) * self._box.ymax
pos_text = self.view((r_txt, theta)) pos_text = self.view((r_txt, theta))
text = self.svg.node( text = self.svg.node(
guides, 'text', guides, 'text',
x=pos_text[0], x=pos_text[0],
y=pos_text[1]) y=pos_text[1],
class_='major' if major else '')
text.text = label text.text = label
angle = - theta + pi / 2 angle = - theta + pi / 2
if cos(angle) < 0: if cos(angle) < 0:

Loading…
Cancel
Save