From 77a55bb10af7e06405385e79360548729db7c285 Mon Sep 17 00:00:00 2001 From: Arjen Stolk Date: Sat, 9 Mar 2013 14:15:38 +0100 Subject: [PATCH] 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. --- pygal/config.py | 21 +++++++++++++++++---- pygal/css/base.css | 5 +++++ pygal/graph/graph.py | 22 ++++++++++++++++++++-- pygal/graph/line.py | 13 ++----------- pygal/graph/radar.py | 24 ++++++++++++++++++++++-- 5 files changed, 66 insertions(+), 19 deletions(-) diff --git a/pygal/config.py b/pygal/config.py index 92042f7..9a6343f 100644 --- a/pygal/config.py +++ b/pygal/config.py @@ -140,10 +140,21 @@ class Config(object): "Leave it to None to disable x labels display.", str) - x_labels_num_limit = Key(None, int, "Label", - "Limits the number of X labels, defaults to None", - "If none, there will be as many x labels as provided in x_labels. When provided - limits the number of X labels" - ) + x_labels_major = Key( + None, list, "Label", + "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( None, list, "Label", @@ -198,6 +209,8 @@ class Config(object): "No data", str, "Text", "Text to display when no data is given") 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") diff --git a/pygal/css/base.css b/pygal/css/base.css index 8d5a65b..a1ccfb0 100644 --- a/pygal/css/base.css +++ b/pygal/css/base.css @@ -37,6 +37,11 @@ font-size: {{ font_sizes.label }}; } +.axis text.major { + font-family: monospace; + font-size: {{ font_sizes.major_label }}; +} + .series text { font-family: monospace; font-size: {{ font_sizes.value }}; diff --git a/pygal/graph/graph.py b/pygal/graph/graph.py index 6eda135..d62c9c1 100644 --- a/pygal/graph/graph.py +++ b/pygal/graph/graph.py @@ -131,8 +131,25 @@ class Graph(BaseGraph): d='M%f %f v%f' % (0, 0, self.view.height), class_='line') 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: - 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') x = self.view.x(position) y = self.view.height + 5 @@ -162,7 +179,8 @@ class Graph(BaseGraph): secondary_ax = self.svg.node( self.nodes['plot'], class_="axis x x2") 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 guides = self.svg.node(secondary_ax, class_='guides') x = self.view.x(position) diff --git a/pygal/graph/line.py b/pygal/graph/line.py index 5f856c8..5a74951 100644 --- a/pygal/graph/line.py +++ b/pygal/graph/line.py @@ -87,10 +87,7 @@ class Line(Graph): self.svg.node(dots, 'circle', cx=x, cy=y, r=self.dots_size, class_='dot reactive tooltip-trigger') self._tooltip_data( - dots, "%s: %s" % ( - self.x_labels[i], val) - if self.x_labels and self.x_labels_num_limit - else val, x, y) + dots, val, x, y) self._static_value( serie_node, val, x + self.value_font_size, @@ -114,13 +111,7 @@ class Line(Graph): self._points(x_pos) if self.x_labels: - 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 + self._x_labels = zip(self.x_labels, x_pos) else: self._x_labels = None diff --git a/pygal/graph/radar.py b/pygal/graph/radar.py index 6444b50..1665c8e 100644 --- a/pygal/graph/radar.py +++ b/pygal/graph/radar.py @@ -72,19 +72,39 @@ class Radar(Line): format_ = lambda x: '%f %f' % x center = self.view((0, 0)) 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: + major = label in x_labels_major + if not (self.show_minor_x_labels or major): continue guides = self.svg.node(axis, class_='guides') end = self.view((r, theta)) self.svg.node( guides, 'path', 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 pos_text = self.view((r_txt, theta)) text = self.svg.node( guides, 'text', x=pos_text[0], - y=pos_text[1]) + y=pos_text[1], + class_='major' if major else '') text.text = label angle = - theta + pi / 2 if cos(angle) < 0: