def _x_axis(self, draw_axes=True): """Make the x axis: labels and guides""" if not self._x_labels: return axis = self.svg.node(self.nodes["plot"], class_="axis x") truncation = self.truncate_label if not truncation: if self.x_label_rotation or len(self._x_labels) <= 1: truncation = 25 else: first_label_position = self.view.x(self._x_labels[0][1]) last_label_position = self.view.x(self._x_labels[-1][1]) available_space = (last_label_position - first_label_position) / (len(self._x_labels) - 1) truncation = int(reverse_text_len(available_space, self.label_font_size)) if 0 not in [label[1] for label in self._x_labels] and draw_axes: self.svg.node(axis, "path", d="M%f %f v%f" % (0, 0, self.view.height), class_="line") for label, position in self._x_labels: guides = self.svg.node(axis, class_="guides") x = self.view.x(position) y = self.view.height + 5 if draw_axes: self.svg.node( guides, "path", d="M%f %f v%f" % (x, 0, self.view.height), class_="%sline" % ("guide " if position != 0 else ""), ) text = self.svg.node(guides, "text", x=x, y=y + 0.5 * self.label_font_size + 5) text.text = truncate(label, truncation) if text.text != label: self.svg.node(guides, "title").text = label if self.x_label_rotation: text.attrib["transform"] = "rotate(%d %f %f)" % (self.x_label_rotation, x, y)
def _x_axis(self, draw_axes=True): """Override x axis to make it polar""" if not self._x_labels or not self.show_x_labels: return axis = self.svg.node(self.nodes['plot'], class_="axis x web%s" % (' always_show' if self.show_x_guides else '')) format_ = lambda x: '%f %f' % x center = self.view((0, 0)) r = self._rmax truncation = self.truncate_label if not truncation: if self.x_label_rotation or len(self._x_labels) <= 1: truncation = 25 else: first_label_position = self.view.x(self._x_labels[0][1]) or 0 last_label_position = self.view.x(self._x_labels[-1][1]) or 0 available_space = (last_label_position - first_label_position ) / (len(self._x_labels) - 1) truncation = reverse_text_len(available_space, self.style.label_font_size) truncation = max(truncation, 1) for label, theta in self._x_labels: major = label in self._x_major_labels 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_='%s%sline' % ('axis ' if label == "0" else '', '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], class_='major' if major else '') text.text = truncate(label, truncation) if text.text != label: self.svg.node(guides, 'title').text = label else: self.svg.node( guides, 'title', ).text = self._x_format(theta) angle = -theta + pi / 2 if cos(angle) < 0: angle -= pi text.attrib['transform'] = 'rotate(%f %s)' % ( self.x_label_rotation or deg(angle), format_(pos_text))
def _title(self): """Make the title""" if self.title: self.svg.node(self.nodes['graph'], 'text', class_='title', x=self.margin.left + self.view.width / 2, y=self.title_font_size + 10 ).text = truncate(self.title, int(reverse_text_len( self.width, self.title_font_size)))
def _legend(self): """Make the legend box""" if not self.show_legend: return truncation = self.truncate_legend if self.legend_at_bottom: x = self.margin.left + 10 y = (self.margin.top + self.view.height + self._x_labels_height + 10) cols = ceil(sqrt(self._order)) if not truncation: available_space = self.view.width / cols - ( self.legend_box_size + 5) truncation = reverse_text_len( available_space, self.legend_font_size) else: x = self.margin.left + self.view.width + 10 y = self.margin.top + 10 cols = 1 if not truncation: truncation = 15 legends = self.svg.node( self.nodes['graph'], class_='legends', transform='translate(%d, %d)' % (x, y)) h = max(self.legend_box_size, self.legend_font_size) x_step = self.view.width / cols for i, title in enumerate(self._legends): col = i % cols row = i // cols legend = self.svg.node( legends, class_='legend reactive activate-serie', id="activate-serie-%d" % i) self.svg.node( legend, 'rect', x=col * x_step, y=1.5 * row * h + ( self.legend_font_size - self.legend_box_size if self.legend_font_size > self.legend_box_size else 0 ) / 2, width=self.legend_box_size, height=self.legend_box_size, class_="color-%d reactive" % (i % 16) ) truncated = truncate(title, truncation) # Serious magical numbers here self.svg.node( legend, 'text', x=col * x_step + self.legend_box_size + 5, y=1.5 * row * h + .5 * h + .3 * self.legend_font_size ).text = truncated if truncated != title: self.svg.node(legend, 'title').text = title
def _legend(self): """Make the legend box""" if not self.show_legend: return truncation = self.truncate_legend if self.legend_at_bottom: x = self.margin.left + 10 y = (self.margin.top + self.view.height + self._x_labels_height + 10) cols = ceil(sqrt(self._order)) if not truncation: available_space = self.view.width / cols - ( self.legend_box_size + 5) truncation = int(reverse_text_len( available_space, self.legend_font_size)) else: x = self.margin.left + self.view.width + 10 y = self.margin.top + 10 cols = 1 if not truncation: truncation = 15 legends = self.svg.node( self.nodes['graph'], class_='legends', transform='translate(%d, %d)' % (x, y)) h = max(self.legend_box_size, self.legend_font_size) x_step = self.view.width / cols for i, title in enumerate(self._legends): col = i % cols row = i // cols legend = self.svg.node( legends, class_='legend reactive activate-serie', id="activate-serie-%d" % i) self.svg.node( legend, 'rect', x=col * x_step, y=1.5 * row * h + ( self.legend_font_size - self.legend_box_size if self.legend_font_size > self.legend_box_size else 0 ) / 2, width=self.legend_box_size, height=self.legend_box_size, class_="color-%d reactive" % (i % 16) ) truncated = truncate(title, truncation) # Serious magical numbers here self.svg.node( legend, 'text', x=col * x_step + self.legend_box_size + 5, y=1.5 * row * h + .5 * h + .3 * self.legend_font_size ).text = truncated if truncated != title: self.svg.node(legend, 'title').text = title
def _title(self): """Make the title""" if self.title: self.svg.node( self.nodes["graph"], "text", class_="title", x=self.margin.left + self.view.width / 2, y=self.title_font_size + 10, ).text = truncate(self.title, int(reverse_text_len(self.width, self.title_font_size)))
def _split_title(self): if not self.title: self.title = [] return size = reverse_text_len(self.width, self.title_font_size) title = self.title.strip() self.title = [] while len(title) > size: title_part = title[:size] i = title_part.rfind(' ') if i == -1: i = len(title_part) self.title.append(title_part[:i]) title = title[i:].strip() self.title.append(title)
def _x_axis(self, draw_axes=True): """Make the x axis: labels and guides""" if not self._x_labels: return axis = self.svg.node(self.nodes['plot'], class_="axis x") truncation = self.truncate_label if not truncation: if self.x_label_rotation or len(self._x_labels) <= 1: truncation = 25 else: first_label_position = self.view.x(self._x_labels[0][1]) last_label_position = self.view.x(self._x_labels[-1][1]) available_space = ( last_label_position - first_label_position) / ( len(self._x_labels) - 1) truncation = reverse_text_len( available_space, self.label_font_size) if 0 not in [label[1] for label in self._x_labels] and draw_axes: self.svg.node(axis, 'path', d='M%f %f v%f' % (0, 0, self.view.height), class_='line') for label, position in self._x_labels: major = is_major(position) guides = self.svg.node(axis, class_='guides') x = self.view.x(position) y = self.view.height + 5 if draw_axes: self.svg.node( guides, 'path', d='M%f %f v%f' % (x, 0, self.view.height), class_='%s%sline' % ( 'major ' if major else '', 'guide ' if position != 0 else '')) y += .5 * self.label_font_size + 5 text = self.svg.node( guides, 'text', x=x, y=y, class_='major' if major else '' ) text.text = truncate(label, truncation) if text.text != label: self.svg.node(guides, 'title').text = label if self.x_label_rotation: text.attrib['transform'] = "rotate(%d %f %f)" % ( self.x_label_rotation, x, y)
def _x_axis(self, draw_axes=True): """Make the x axis: labels and guides""" if not self._x_labels: return axis = self.svg.node(self.nodes['plot'], class_="axis x") truncation = self.truncate_label if not truncation: if self.x_label_rotation or len(self._x_labels) <= 1: truncation = 25 else: first_label_position = self.view.x(self._x_labels[0][1]) last_label_position = self.view.x(self._x_labels[-1][1]) available_space = (last_label_position - first_label_position ) / (len(self._x_labels) - 1) truncation = reverse_text_len(available_space, self.label_font_size) if 0 not in [label[1] for label in self._x_labels] and draw_axes: self.svg.node(axis, 'path', d='M%f %f v%f' % (0, 0, self.view.height), class_='line') for label, position in self._x_labels: major = is_major(position) guides = self.svg.node(axis, class_='guides') x = self.view.x(position) y = self.view.height + 5 if draw_axes: self.svg.node(guides, 'path', d='M%f %f v%f' % (x, 0, self.view.height), class_='%s%sline' % ('major ' if major else '', 'guide ' if position != 0 else '')) y += .5 * self.label_font_size + 5 text = self.svg.node(guides, 'text', x=x, y=y, class_='major' if major else '') text.text = truncate(label, truncation) if text.text != label: self.svg.node(guides, 'title').text = label if self.x_label_rotation: text.attrib['transform'] = "rotate(%d %f %f)" % ( self.x_label_rotation, x, y)
def _legend(self): """Make the legend box""" if not self.show_legend: return truncation = self.truncate_legend if self.legend_at_bottom: x = self.margin.left + self.spacing y = (self.margin.top + self.view.height + self._x_title_height + self._x_labels_height + self.spacing) cols = self.legend_at_bottom_columns or ceil(sqrt( self._order)) or 1 if not truncation: available_space = self.view.width / cols - ( self.legend_box_size + 5) truncation = reverse_text_len(available_space, self.legend_font_size) else: x = self.spacing y = self.margin.top + self.spacing cols = 1 if not truncation: truncation = 15 legends = self.svg.node(self.nodes['graph'], class_='legends', transform='translate(%d, %d)' % (x, y)) h = max(self.legend_box_size, self.legend_font_size) x_step = self.view.width / cols if self.legend_at_bottom: # if legends at the bottom, we dont split the windows # gen structure - (i, (j, (l, tf))) # i - global serie number - used for coloring and identification # j - position within current legend box # l - label # tf - whether it is secondary label gen = enumerate( enumerate( chain(zip(self._legends, repeat(False)), zip(self._secondary_legends, repeat(True))))) secondary_legends = legends # svg node is the same else: gen = enumerate( chain(enumerate(zip(self._legends, repeat(False))), enumerate(zip(self._secondary_legends, repeat(True))))) # draw secondary axis on right x = self.margin.left + self.view.width + self.spacing if self._y_2nd_labels: h, w = get_texts_box(cut(self._y_2nd_labels), self.label_font_size) x += self.spacing + max(w * cos(rad(self.y_label_rotation)), h) y = self.margin.top + self.spacing secondary_legends = self.svg.node(self.nodes['graph'], class_='legends', transform='translate(%d, %d)' % (x, y)) for (global_serie_number, (i, (title, is_secondary))) in gen: col = i % cols row = i // cols legend = self.svg.node( secondary_legends if is_secondary else legends, class_='legend reactive activate-serie', id="activate-serie-%d" % global_serie_number) self.svg.node( legend, 'rect', x=col * x_step, y=1.5 * row * h + (self.legend_font_size - self.legend_box_size if self.legend_font_size > self.legend_box_size else 0) / 2, width=self.legend_box_size, height=self.legend_box_size, class_="color-%d reactive" % (global_serie_number % len(self.style['colors']))) if isinstance(title, dict): node = decorate(self.svg, legend, title) title = title['title'] else: node = legend truncated = truncate(title, truncation) self.svg.node(node, 'text', x=col * x_step + self.legend_box_size + 5, y=1.5 * row * h + .5 * h + .3 * self.legend_font_size).text = truncated if truncated != title: self.svg.node(legend, 'title').text = title
def _x_axis(self): """Make the x axis: labels and guides""" if not self._x_labels or not self.show_x_labels: return axis = self.svg.node(self.nodes['plot'], class_="axis x%s" % (' always_show' if self.show_x_guides else '')) truncation = self.truncate_label if not truncation: if self.x_label_rotation or len(self._x_labels) <= 1: truncation = 25 else: first_label_position = self.view.x(self._x_labels[0][1]) or 0 last_label_position = self.view.x(self._x_labels[-1][1]) or 0 available_space = (last_label_position - first_label_position ) / (len(self._x_labels) - 1) truncation = reverse_text_len(available_space, self.label_font_size) truncation = max(truncation, 1) if 0 not in [label[1] for label in self._x_labels]: self.svg.node(axis, 'path', d='M%f %f v%f' % (0, 0, self.view.height), class_='line') lastlabel = self._x_labels[-1][0] for label, position in self._x_labels: major = label in self._x_major_labels 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 last_guide = (self._y_2nd_labels and label == lastlabel) self.svg.node( guides, 'path', d='M%f %f v%f' % (x or 0, 0, self.view.height), class_='%s%s%sline' % ('axis ' if label == "0" else '', 'major ' if major else '', 'guide ' if position != 0 and not last_guide else '')) y += .5 * self.label_font_size + 5 text = self.svg.node(guides, 'text', x=x, y=y, class_='major' if major else '') if isinstance(label, dict): label = label['title'] text.text = truncate(label, truncation) if text.text != label: self.svg.node(guides, 'title').text = label if self.x_label_rotation: text.attrib['transform'] = "rotate(%d %f %f)" % ( self.x_label_rotation, x, y) if self._x_2nd_labels: secondary_ax = self.svg.node( self.nodes['plot'], class_="axis x x2%s" % (' always_show' if self.show_x_guides else '')) for label, position in self._x_2nd_labels: major = label in self._x_major_labels 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) y = -5 text = self.svg.node(guides, 'text', x=x, y=y, class_='major' if major else '') text.text = label if self.x_label_rotation: text.attrib['transform'] = "rotate(%d %f %f)" % ( -self.x_label_rotation, x, y)
def _legend(self): """Make the legend box""" if not self.show_legend: return truncation = self.truncate_legend if self.legend_at_bottom: x = self.margin_box.left + self.spacing y = ( self.margin_box.top + self.view.height + self._x_title_height + self._x_labels_height + self.spacing ) cols = self.legend_at_bottom_columns or ceil(sqrt(self._order) ) or 1 if not truncation: available_space = self.view.width / cols - ( self.legend_box_size + 5 ) truncation = reverse_text_len( available_space, self.style.legend_font_size ) else: x = self.spacing y = self.margin_box.top + self.spacing cols = 1 if not truncation: truncation = 15 legends = self.svg.node( self.nodes['graph'], class_='legends', transform='translate(%d, %d)' % (x, y) ) h = max(self.legend_box_size, self.style.legend_font_size) x_step = self.view.width / cols if self.legend_at_bottom: secondary_legends = legends # svg node is the same else: # draw secondary axis on right x = self.margin_box.left + self.view.width + self.spacing if self._y_2nd_labels: h, w = get_texts_box( cut(self._y_2nd_labels), self.style.label_font_size ) x += self.spacing + max( w * abs(cos(rad(self.y_label_rotation))), h ) y = self.margin_box.top + self.spacing secondary_legends = self.svg.node( self.nodes['graph'], class_='legends', transform='translate(%d, %d)' % (x, y) ) serie_number = -1 i = 0 for titles, is_secondary in ((self._legends, False), (self._secondary_legends, True)): if not self.legend_at_bottom and is_secondary: i = 0 for title in titles: serie_number += 1 if title is None: continue col = i % cols row = i // cols legend = self.svg.node( secondary_legends if is_secondary else legends, class_='legend reactive activate-serie', id="activate-serie-%d" % serie_number ) self.svg.node( legend, 'rect', x=col * x_step, y=1.5 * row * h + ( self.style.legend_font_size - self.legend_box_size if self.style.legend_font_size > self.legend_box_size else 0 ) / 2, width=self.legend_box_size, height=self.legend_box_size, class_="color-%d reactive" % serie_number ) if isinstance(title, dict): node = decorate(self.svg, legend, title) title = title['title'] else: node = legend truncated = truncate(title, truncation) self.svg.node( node, 'text', x=col * x_step + self.legend_box_size + 5, y=1.5 * row * h + .5 * h + .3 * self.style.legend_font_size ).text = truncated if truncated != title: self.svg.node(legend, 'title').text = title i += 1
def _x_axis(self): """Make the x axis: labels and guides""" if not self._x_labels: return axis = self.svg.node(self.nodes["plot"], class_="axis x%s" % (" always_show" if self.show_x_guides else "")) truncation = self.truncate_label if not truncation: if self.x_label_rotation or len(self._x_labels) <= 1: truncation = 25 else: first_label_position = self.view.x(self._x_labels[0][1]) last_label_position = self.view.x(self._x_labels[-1][1]) available_space = (last_label_position - first_label_position) / (len(self._x_labels) - 1) truncation = reverse_text_len(available_space, self.label_font_size) if 0 not in [label[1] for label in self._x_labels]: self.svg.node(axis, "path", 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 range(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 range(major_count) ] else: x_labels_major = [] for label, position 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") x = self.view.x(position) y = self.view.height + 5 last_guide = self._y_2nd_labels and label == lastlabel self.svg.node( guides, "path", d="M%f %f v%f" % (x, 0, self.view.height), class_="%s%sline" % ("major " if major else "", "guide " if position != 0 and not last_guide else ""), ) y += 0.5 * self.label_font_size + 5 text = self.svg.node(guides, "text", x=x, y=y, class_="major" if major else "") text.text = truncate(label, truncation) if text.text != label: self.svg.node(guides, "title").text = label if self.x_label_rotation: text.attrib["transform"] = "rotate(%d %f %f)" % (self.x_label_rotation, x, y) if self._x_2nd_labels: secondary_ax = self.svg.node( self.nodes["plot"], class_="axis x x2%s" % (" always_show" if self.show_x_guides else "") ) for label, position in self._x_2nd_labels: 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) y = -5 text = self.svg.node(guides, "text", x=x, y=y, class_="major" if major else "") text.text = label if self.x_label_rotation: text.attrib["transform"] = "rotate(%d %f %f)" % (-self.x_label_rotation, x, y)
def _x_axis(self): """Make the x axis: labels and guides""" if not self._x_labels or not self.show_x_labels: return axis = self.svg.node(self.nodes['plot'], class_="axis x%s" % (' always_show' if self.show_x_guides else '')) truncation = self.truncate_label if not truncation: if self.x_label_rotation or len(self._x_labels) <= 1: truncation = 25 else: first_label_position = self.view.x(self._x_labels[0][1]) or 0 last_label_position = self.view.x(self._x_labels[-1][1]) or 0 available_space = (last_label_position - first_label_position ) / (len(self._x_labels) - 1) truncation = reverse_text_len(available_space, self.style.label_font_size) truncation = max(truncation, 1) lastlabel = self._x_labels[-1][0] if 0 not in [label[1] for label in self._x_labels]: self.svg.node(axis, 'path', d='M%s %s v%s' % float_format(0, 0, self.view.height), class_='line') lastlabel = None for label, position in self._x_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') x = self.view.x(position) if x is None: continue y = self.view.height + 5 last_guide = (self._y_2nd_labels and label == lastlabel) self.svg.node( guides, 'path', d='M%s %s v%s' % float_format(x or 0, 0, self.view.height), class_='%s%s%sline' % ('axis ' if label == "0" else '', 'major ' if major else '', 'guide ' if position != 0 and not last_guide else '')) y += .5 * self.style.label_font_size + 5 text = self.svg.node(guides, 'text', x=x, y=y, class_='major' if major else '') text.text = truncate(label, truncation) if text.text != label: self.svg.node(guides, 'title').text = label elif self._dual: self.svg.node( guides, 'title', ).text = self._x_format(position) if self.x_label_rotation: text.attrib['transform'] = "rotate(%d %s %s)" % ( self.x_label_rotation, float_format(x), float_format(y)) if self.x_label_rotation >= 180: text.attrib['class'] = ' '.join( (text.attrib['class'] and text.attrib['class'].split(' ') or []) + ['backwards']) if self._y_2nd_labels and 0 not in [ label[1] for label in self._x_labels ]: self.svg.node(axis, 'path', d='M%f %f v%f' % (self.view.width, 0, self.view.height), class_='line') if self._x_2nd_labels: secondary_ax = self.svg.node( self.nodes['plot'], class_="axis x x2%s" % (' always_show' if self.show_x_guides else '')) for label, position in self._x_2nd_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 guides = self.svg.node(secondary_ax, class_='guides') x = self.view.x(position) y = -5 text = self.svg.node(guides, 'text', x=x, y=y, class_='major' if major else '') text.text = label if self.x_label_rotation: text.attrib['transform'] = "rotate(%d %s %s)" % ( -self.x_label_rotation, float_format(x), float_format(y)) if self.x_label_rotation >= 180: text.attrib['class'] = ' '.join( (text.attrib['class'] and text.attrib['class'].split(' ') or []) + ['backwards'])
def _x_axis(self): """Make the x axis: labels and guides""" if not self._x_labels: return axis = self.svg.node(self.nodes['plot'], class_="axis x%s" % ( ' always_show' if self.show_x_guides else '' )) truncation = self.truncate_label if not truncation: if self.x_label_rotation or len(self._x_labels) <= 1: truncation = 25 else: first_label_position = self.view.x(self._x_labels[0][1]) or 0 last_label_position = self.view.x(self._x_labels[-1][1]) or 0 available_space = ( last_label_position - first_label_position) / ( len(self._x_labels) - 1) truncation = reverse_text_len( available_space, self.label_font_size) truncation = max(truncation, 1) if 0 not in [label[1] for label in self._x_labels]: self.svg.node(axis, 'path', d='M%f %f v%f' % (0, 0, self.view.height), class_='line') lastlabel = self._x_labels[-1][0] for label, position in self._x_labels: major = label in self._x_major_labels 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 last_guide = (self._y_2nd_labels and label == lastlabel) self.svg.node( guides, 'path', d='M%f %f v%f' % (x or 0, 0, self.view.height), class_='%s%sline' % ( 'major ' if major else '', 'guide ' if position != 0 and not last_guide else '')) y += .5 * self.label_font_size + 5 text = self.svg.node( guides, 'text', x=x, y=y, class_='major' if major else '' ) if isinstance(label, dict): label = label['title'] text.text = truncate(label, truncation) if text.text != label: self.svg.node(guides, 'title').text = label if self.x_label_rotation: text.attrib['transform'] = "rotate(%d %f %f)" % ( self.x_label_rotation, x, y) if self._x_2nd_labels: secondary_ax = self.svg.node( self.nodes['plot'], class_="axis x x2%s" % ( ' always_show' if self.show_x_guides else '' )) for label, position in self._x_2nd_labels: major = label in self._x_major_labels 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) y = -5 text = self.svg.node( guides, 'text', x=x, y=y, class_='major' if major else '' ) text.text = label if self.x_label_rotation: text.attrib['transform'] = "rotate(%d %f %f)" % ( -self.x_label_rotation, x, y)
def _x_axis(self, draw_axes=True): """Make the x axis: labels and guides""" if not self._x_labels: return axis = self.svg.node(self.nodes['plot'], class_="axis x") truncation = self.truncate_label if not truncation: if self.x_label_rotation or len(self._x_labels) <= 1: truncation = 25 else: first_label_position = self.view.x(self._x_labels[0][1]) last_label_position = self.view.x(self._x_labels[-1][1]) available_space = (last_label_position - first_label_position ) / (len(self._x_labels) - 1) truncation = reverse_text_len(available_space, self.label_font_size) if 0 not in [label[1] for label in self._x_labels] and draw_axes: self.svg.node(axis, 'path', 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 = 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 if draw_axes: last_guide = (self._y_2nd_labels and label == lastlabel) self.svg.node( guides, 'path', d='M%f %f v%f' % (x, 0, self.view.height), class_='%s%sline' % ('major ' if major else '', 'guide ' if position != 0 and not last_guide else '')) y += .5 * self.label_font_size + 5 text = self.svg.node(guides, 'text', x=x, y=y, class_='major' if major else '') text.text = truncate(label, truncation) if text.text != label: self.svg.node(guides, 'title').text = label if self.x_label_rotation: text.attrib['transform'] = "rotate(%d %f %f)" % ( self.x_label_rotation, x, y) if self._x_2nd_labels: secondary_ax = self.svg.node(self.nodes['plot'], class_="axis x x2") for label, position in self._x_2nd_labels: 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) y = -5 text = self.svg.node(guides, 'text', x=x, y=y, class_='major' if major else '') text.text = label if self.x_label_rotation: text.attrib['transform'] = "rotate(%d %f %f)" % ( -self.x_label_rotation, x, y)
def _legend(self): """Make the legend box""" if not self.show_legend: return truncation = self.truncate_legend if self.legend_at_bottom: x = self.margin_box.left + self.spacing y = (self.margin_box.top + self.view.height + self._x_title_height + self._x_labels_height + self.spacing) cols = self.legend_at_bottom_columns or ceil( sqrt(self._order)) or 1 if not truncation: available_space = self.view.width / cols - ( self.legend_box_size + 5) truncation = reverse_text_len( available_space, self.style.legend_font_size) else: x = self.spacing y = self.margin_box.top + self.spacing cols = 1 if not truncation: truncation = 15 legends = self.svg.node( self.nodes['graph'], class_='legends', transform='translate(%d, %d)' % (x, y)) h = max(self.legend_box_size, self.style.legend_font_size) x_step = self.view.width / cols if self.legend_at_bottom: secondary_legends = legends # svg node is the same else: # draw secondary axis on right x = self.margin_box.left + self.view.width + self.spacing if self._y_2nd_labels: h, w = get_texts_box( cut(self._y_2nd_labels), self.style.label_font_size) x += self.spacing + max(w * abs(cos(rad( self.y_label_rotation))), h) y = self.margin_box.top + self.spacing secondary_legends = self.svg.node( self.nodes['graph'], class_='legends', transform='translate(%d, %d)' % (x, y)) serie_number = -1 i = 0 for titles, is_secondary in ( (self._legends, False), (self._secondary_legends, True)): if not self.legend_at_bottom and is_secondary: i = 0 for title in titles: serie_number += 1 if title is None: continue col = i % cols row = i // cols legend = self.svg.node( secondary_legends if is_secondary else legends, class_='legend reactive activate-serie', id="activate-serie-%d" % serie_number) self.svg.node( legend, 'rect', x=col * x_step, y=1.5 * row * h + ( self.style.legend_font_size - self.legend_box_size if self.style.legend_font_size > self.legend_box_size else 0 ) / 2, width=self.legend_box_size, height=self.legend_box_size, class_="color-%d reactive" % ( serie_number % len(self.style.colors)) ) if isinstance(title, dict): node = decorate(self.svg, legend, title) title = title['title'] else: node = legend truncated = truncate(title, truncation) self.svg.node( node, 'text', x=col * x_step + self.legend_box_size + 5, y=1.5 * row * h + .5 * h + .3 * self.style.legend_font_size ).text = truncated if truncated != title: self.svg.node(legend, 'title').text = title i += 1
def _x_axis(self): """Make the x axis: labels and guides""" if not self._x_labels or not self.show_x_labels: return axis = self.svg.node(self.nodes['plot'], class_="axis x%s" % ( ' always_show' if self.show_x_guides else '' )) truncation = self.truncate_label if not truncation: if self.x_label_rotation or len(self._x_labels) <= 1: truncation = 25 else: first_label_position = self.view.x(self._x_labels[0][1]) or 0 last_label_position = self.view.x(self._x_labels[-1][1]) or 0 available_space = ( last_label_position - first_label_position) / ( len(self._x_labels) - 1) truncation = reverse_text_len( available_space, self.style.label_font_size) truncation = max(truncation, 1) lastlabel = self._x_labels[-1][0] if 0 not in [label[1] for label in self._x_labels]: self.svg.node(axis, 'path', d='M%f %f v%f' % (0, 0, self.view.height), class_='line') lastlabel = None for label, position in self._x_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') x = self.view.x(position) if x is None: continue y = self.view.height + 5 last_guide = (self._y_2nd_labels and label == lastlabel) self.svg.node( guides, 'path', d='M%f %f v%f' % (x or 0, 0, self.view.height), class_='%s%s%sline' % ( 'axis ' if label == "0" else '', 'major ' if major else '', 'guide ' if position != 0 and not last_guide else '')) y += .5 * self.style.label_font_size + 5 text = self.svg.node( guides, 'text', x=x, y=y, class_='major' if major else '' ) text.text = truncate(label, truncation) if text.text != label: self.svg.node(guides, 'title').text = label elif self._dual: self.svg.node( guides, 'title', ).text = self._x_format(position) if self.x_label_rotation: text.attrib['transform'] = "rotate(%d %f %f)" % ( self.x_label_rotation, x, y) if self.x_label_rotation >= 180: text.attrib['class'] = ' '.join( (text.attrib['class'] and text.attrib['class'].split( ' ') or []) + ['backwards']) if self._y_2nd_labels and 0 not in [ label[1] for label in self._x_labels]: self.svg.node(axis, 'path', d='M%f %f v%f' % ( self.view.width, 0, self.view.height), class_='line') if self._x_2nd_labels: secondary_ax = self.svg.node( self.nodes['plot'], class_="axis x x2%s" % ( ' always_show' if self.show_x_guides else '' )) for label, position in self._x_2nd_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 guides = self.svg.node(secondary_ax, class_='guides') x = self.view.x(position) y = -5 text = self.svg.node( guides, 'text', x=x, y=y, class_='major' if major else '' ) text.text = label if self.x_label_rotation: text.attrib['transform'] = "rotate(%d %f %f)" % ( -self.x_label_rotation, x, y) if self.x_label_rotation >= 180: text.attrib['class'] = ' '.join(( text.attrib['class'] and text.attrib['class'].split( ' ') or []) + ['backwards'])
def _legend(self): """Make the legend box""" if not self.show_legend: return truncation = self.truncate_legend if self.legend_at_bottom: x = self.margin.left + self.spacing y = (self.margin.top + self.view.height + self._x_title_height + self._x_labels_height + self.spacing) cols = ceil(sqrt(self._order)) or 1 if not truncation: available_space = self.view.width / cols - ( self.legend_box_size + 5) truncation = reverse_text_len( available_space, self.legend_font_size) else: x = self.spacing y = self.margin.top + self.spacing cols = 1 if not truncation: truncation = 15 legends = self.svg.node( self.nodes['graph'], class_='legends', transform='translate(%d, %d)' % (x, y)) h = max(self.legend_box_size, self.legend_font_size) x_step = self.view.width / cols if self.legend_at_bottom: # if legends at the bottom, we dont split the windows # gen structure - (i, (j, (l, tf))) # i - global serie number - used for coloring and identification # j - position within current legend box # l - label # tf - whether it is secondary label gen = enumerate(enumerate(chain( zip(self._legends, repeat(False)), zip(self._secondary_legends, repeat(True))))) secondary_legends = legends # svg node is the same else: gen = enumerate(chain( enumerate(zip(self._legends, repeat(False))), enumerate(zip(self._secondary_legends, repeat(True))))) # draw secondary axis on right x = self.margin.left + self.view.width + self.spacing if self._y_2nd_labels: h, w = get_texts_box( cut(self._y_2nd_labels), self.label_font_size) x += self.spacing + max(w * cos(rad(self.y_label_rotation)), h) y = self.margin.top + self.spacing secondary_legends = self.svg.node( self.nodes['graph'], class_='legends', transform='translate(%d, %d)' % (x, y)) for (global_serie_number, (i, (title, is_secondary))) in gen: col = i % cols row = i // cols legend = self.svg.node( secondary_legends if is_secondary else legends, class_='legend reactive activate-serie', id="activate-serie-%d" % global_serie_number) self.svg.node( legend, 'rect', x=col * x_step, y=1.5 * row * h + ( self.legend_font_size - self.legend_box_size if self.legend_font_size > self.legend_box_size else 0 ) / 2, width=self.legend_box_size, height=self.legend_box_size, class_="color-%d reactive" % ( global_serie_number % len(self.style['colors'])) ) if isinstance(title, dict): node = decorate(self.svg, legend, title) title = title['title'] else: node = legend truncated = truncate(title, truncation) self.svg.node( node, 'text', x=col * x_step + self.legend_box_size + 5, y=1.5 * row * h + .5 * h + .3 * self.legend_font_size ).text = truncated if truncated != title: self.svg.node(legend, 'title').text = title
def _x_axis(self, draw_axes=True): """Make the x axis: labels and guides""" if not self._x_labels: return axis = self.svg.node(self.nodes['plot'], class_="axis x") truncation = self.truncate_label if not truncation: if self.x_label_rotation or len(self._x_labels) <= 1: truncation = 25 else: first_label_position = self.view.x(self._x_labels[0][1]) last_label_position = self.view.x(self._x_labels[-1][1]) available_space = ( last_label_position - first_label_position) / ( len(self._x_labels) - 1) truncation = reverse_text_len( available_space, self.label_font_size) if 0 not in [label[1] for label in self._x_labels] and draw_axes: self.svg.node(axis, 'path', 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 = 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 if draw_axes: last_guide = (self._y_2nd_labels and label == lastlabel) self.svg.node( guides, 'path', d='M%f %f v%f' % (x, 0, self.view.height), class_='%s%sline' % ( 'major ' if major else '', 'guide ' if position != 0 and not last_guide else '')) y += .5 * self.label_font_size + 5 text = self.svg.node( guides, 'text', x=x, y=y, class_='major' if major else '' ) text.text = truncate(label, truncation) if text.text != label: self.svg.node(guides, 'title').text = label if self.x_label_rotation: text.attrib['transform'] = "rotate(%d %f %f)" % ( self.x_label_rotation, x, y) if self._x_2nd_labels: secondary_ax = self.svg.node( self.nodes['plot'], class_="axis x x2") for label, position in self._x_2nd_labels: 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) y = -5 text = self.svg.node( guides, 'text', x=x, y=y, class_='major' if major else '' ) text.text = label if self.x_label_rotation: text.attrib['transform'] = "rotate(%d %f %f)" % ( -self.x_label_rotation, x, y)