diff --git a/demo/moulinrouge/tests.py b/demo/moulinrouge/tests.py index dbe80b0..837d194 100644 --- a/demo/moulinrouge/tests.py +++ b/demo/moulinrouge/tests.py @@ -344,10 +344,11 @@ def get_test_routes(app): @app.route('/test/bar') def test_bar(): - bar = Bar(dynamic_print_values=True) + bar = Bar(dynamic_print_values=True, show_minor_x_labels=False) bar.add('1', [1, 2, 3]) bar.add('2', [4, 5, 6]) - bar.x_labels = ['a'] + bar.x_labels = [2, 4, 6] + bar.x_labels_major = [4] return bar.render_response() @app.route('/test/histogram') @@ -453,7 +454,7 @@ def get_test_routes(app): @app.route('/test/dateline') def test_dateline(): - dateline = DateLine(show_dots=False) + dateline = DateLine(x_label_rotation=25, show_minor_x_labels=False) dateline.x_labels = [ date(2013, 1, 1), date(2013, 7, 1), @@ -462,13 +463,16 @@ def get_test_routes(app): date(2015, 1, 1), date(2015, 7, 1) ] - dateline.add('1', [ - (date(2013, 1, 2), 300), - (date(2014, 1, 12), 412), - (date(2015, 2, 2), 823), - (date(2013, 2, 22), 672) + dateline.x_labels_major = [ + date(2013, 1, 1), + date(2015, 7, 1) + ] + dateline.add("Serie", [ + (date(2013, 1, 2), 213), + (date(2013, 8, 2), 281), + (date(2014, 12, 7), 198), + (date(2015, 3, 21), 120) ]) - dateline.x_label_rotation = 25 return dateline.render_response() @app.route('/test/timeline') @@ -645,18 +649,20 @@ def get_test_routes(app): @app.route('/test/x_major_labels/') def test_x_major_labels_for(chart): - chart = CHARTS_BY_NAME[chart]() + chart = CHARTS_BY_NAME[chart](show_minor_y_labels=False) for i in range(12): chart.add('test', range(12)) - chart.x_labels = map(str, range(10)) + chart.x_labels = map(str, range(12)) # chart.x_labels_major_count = 4 - # chart.x_labels_major = ['1', '5', '11', '1.0', '5.0', '11.0'] + # chart.x_labels_major = ['1', '5', '11', 6] + # chart.y_labels_major = [60, 120] return chart.render_response() @app.route('/test/y_major_labels/') def test_y_major_labels_for(chart): chart = CHARTS_BY_NAME[chart]() - chart.add('test', zip(*[range(12), range(12)])) + chart.add('test', range(12)) + # chart.add('test', zip(*[range(12), range(12)])) chart.y_labels = range(12) # chart.y_labels_major_count = 4 chart.y_labels_major = [1.0, 5.0, 11.0] diff --git a/docs/changelog.rst b/docs/changelog.rst index 8989232..9583469 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -7,6 +7,7 @@ Changelog * Add `dynamic_print_values` to show print_values on legend hover. (Fix #279) * Fix unparse_color for python 3.5+ compatibility (thanks felixonmars, sjourdois) +* Process major labels as labels. (Fix #263) 2.0.8 diff --git a/pygal/graph/dual.py b/pygal/graph/dual.py index ecc2f4b..9d4792a 100644 --- a/pygal/graph/dual.py +++ b/pygal/graph/dual.py @@ -52,6 +52,12 @@ class Dual(Graph): else: self._x_labels = list(zip(map(self._x_format, x_pos), x_pos)) + def _compute_x_labels_major(self): + # In case of dual, x labels must adapters and so majors too + self.x_labels_major = self.x_labels_major and list( + map(self._x_adapt, self.x_labels_major)) + super()._compute_x_labels_major() + def _get_x_label(self, i): """Convenience function to get the x_label of a value index""" return diff --git a/pygal/graph/graph.py b/pygal/graph/graph.py index eb5cebc..a416148 100644 --- a/pygal/graph/graph.py +++ b/pygal/graph/graph.py @@ -146,7 +146,10 @@ class Graph(PublicApi): lastlabel = self._x_labels[-1][0] for label, position in self._x_labels: - major = label in self._x_major_labels + if self.horizontal: + major = position in self._x_labels_major + else: + major = label in self._x_labels_major if not (self.show_minor_x_labels or major): continue guides = self.svg.node(axis, class_='guides') @@ -188,7 +191,7 @@ class Graph(PublicApi): ' always_show' if self.show_x_guides else '' )) for label, position in self._x_2nd_labels: - major = label in self._x_major_labels + major = label in self._x_labels_major if not (self.show_minor_x_labels or major): continue # it is needed, to have the same structure as primary axis @@ -224,7 +227,11 @@ class Graph(PublicApi): ) for label, position in self._y_labels: - major = position in self._y_major_labels + if self.horizontal: + major = label in self._y_labels_major + else: + major = position in self._y_labels_major + if not (self.show_minor_y_labels or major): continue guides = self.svg.node(axis, class_='%sguides' % ( @@ -263,7 +270,7 @@ class Graph(PublicApi): secondary_ax = self.svg.node( self.nodes['plot'], class_="axis y2") for label, position in self._y_2nd_labels: - major = position in self._y_major_labels + major = position in self._y_labels_major if not (self.show_minor_y_labels or major): continue # it is needed, to have the same structure as primary axis @@ -705,57 +712,34 @@ class Graph(PublicApi): """Getter for the number of series""" return len(self.all_series) - @cached_property - def _x_major_labels(self): - """Getter for the x major label""" - if self.x_labels_major: - return self.x_labels_major + def _x_label_format_if_value(self, label): + if not is_str(label): + return self._x_format(label) + return label + + def _compute_x_labels(self): + self._x_labels = self.x_labels and list( + zip(map(self._x_label_format_if_value, self.x_labels), + self._x_pos)) + + def _compute_x_labels_major(self): if self.x_labels_major_every: - return [self._x_labels[i][0] for i in range( + self._x_labels_major = [self._x_labels[i][0] for i in range( 0, len(self._x_labels), self.x_labels_major_every)] - if self.x_labels_major_count: + + elif self.x_labels_major_count: label_count = len(self._x_labels) major_count = self.x_labels_major_count if (major_count >= label_count): - return [label[0] for label in self._x_labels] + self._x_labels_major = [label[0] for label in self._x_labels] - return [self._x_labels[ + else: + self._x_labels_major = [self._x_labels[ int(i * (label_count - 1) / (major_count - 1))][0] for i in range(major_count)] - - return [] - - @cached_property - def _y_major_labels(self): - """Getter for the y major label""" - if self.y_labels_major: - return self.y_labels_major - if self.y_labels_major_every: - return [self._y_labels[i][1] for i in range( - 0, len(self._y_labels), self.y_labels_major_every)] - if self.y_labels_major_count: - label_count = len(self._y_labels) - major_count = self.y_labels_major_count - if (major_count >= label_count): - return [label[1] for label in self._y_labels] - - return [self._y_labels[ - int(i * (label_count - 1) / (major_count - 1))][1] - for i in range(major_count)] - - return majorize( - cut(self._y_labels, 1) - ) - - def _compute_x_labels(self): - - def format_if_value(label): - if not is_str(label): - return self._x_format(label) - return label - - self._x_labels = self.x_labels and list( - zip(map(format_if_value, self.x_labels), self._x_pos)) + else: + self._x_labels_major = self.x_labels_major and list( + map(self._x_label_format_if_value, self.x_labels_major)) or [] def _compute_y_labels(self): y_pos = compute_scale( @@ -780,11 +764,35 @@ class Graph(PublicApi): else: self._y_labels = list(zip(map(self._format, y_pos), y_pos)) + def _compute_y_labels_major(self): + if self.y_labels_major_every: + self._y_labels_major = [self._y_labels[i][1] for i in range( + 0, len(self._y_labels), self.y_labels_major_every)] + + elif self.y_labels_major_count: + label_count = len(self._y_labels) + major_count = self.y_labels_major_count + if (major_count >= label_count): + self._y_labels_major = [label[1] for label in self._y_labels] + else: + self._y_labels_major = [self._y_labels[ + int(i * (label_count - 1) / (major_count - 1))][1] + for i in range(major_count)] + + elif self.y_labels_major: + self._y_labels_major = list(map(self._adapt, self.y_labels_major)) + elif self._y_labels: + self._y_labels_major = majorize(cut(self._y_labels, 1)) + else: + self._y_labels_major = [] + def _draw(self): """Draw all the things""" self._compute() self._compute_x_labels() + self._compute_x_labels_major() self._compute_y_labels() + self._compute_y_labels_major() self._compute_secondary() self._post_compute() self._compute_margin() diff --git a/pygal/graph/horizontal.py b/pygal/graph/horizontal.py index 76b6bbc..a31cca8 100644 --- a/pygal/graph/horizontal.py +++ b/pygal/graph/horizontal.py @@ -34,6 +34,8 @@ class HorizontalGraph(Graph): def _post_compute(self): """After computations transpose labels""" self._x_labels, self._y_labels = self._y_labels, self._x_labels + self._x_labels_major, self._y_labels_major = ( + self._y_labels_major, self._x_labels_major) self._x_2nd_labels, self._y_2nd_labels = ( self._y_2nd_labels, self._x_2nd_labels) diff --git a/pygal/graph/line.py b/pygal/graph/line.py index e26c35d..2fe7527 100644 --- a/pygal/graph/line.py +++ b/pygal/graph/line.py @@ -101,7 +101,7 @@ class Line(Graph): continue if (serie.show_only_major_dots and self.x_labels and i < len(self.x_labels) and - self.x_labels[i] not in self._x_major_labels): + self.x_labels[i] not in self._x_labels_major): continue metadata = serie.metadata.get(i) diff --git a/pygal/graph/radar.py b/pygal/graph/radar.py index a43fc0f..64c633f 100644 --- a/pygal/graph/radar.py +++ b/pygal/graph/radar.py @@ -90,7 +90,7 @@ class Radar(Line): truncation = self.truncate_label or 25 for label, theta in self._x_labels: - major = label in self._x_major_labels + major = label in self._x_labels_major if not (self.show_minor_x_labels or major): continue guides = self.svg.node(axis, class_='guides') @@ -132,7 +132,7 @@ class Radar(Line): axis = self.svg.node(self.nodes['plot'], class_="axis y web") for label, r in reversed(self._y_labels): - major = r in self._y_major_labels + major = r in self._y_labels_major if not (self.show_minor_y_labels or major): continue guides = self.svg.node(axis, class_='%sguides' % ( diff --git a/pygal/test/test_config.py b/pygal/test/test_config.py index 2acf151..4beda58 100644 --- a/pygal/test/test_config.py +++ b/pygal/test/test_config.py @@ -25,6 +25,8 @@ from pygal import ( Pyramid, HorizontalBar, HorizontalStackedBar, DateTimeLine, TimeLine, DateLine, TimeDeltaLine) from pygal.graph.map import BaseMap +from pygal.graph.horizontal import HorizontalGraph +from pygal.graph.dual import Dual from pygal._compat import u from pygal.test.utils import texts from tempfile import NamedTemporaryFile @@ -388,7 +390,7 @@ def test_x_label_major(Chart): Pie, Treemap, Funnel, Dot, Gauge, Histogram, Box, Pyramid, DateTimeLine, TimeLine, DateLine, TimeDeltaLine - ) or issubclass(Chart, BaseMap) or Chart._dual: + ) or issubclass(Chart, (BaseMap, Dual, HorizontalGraph)): return chart = Chart() chart.add('test', range(12))