Pygal:更改圆点类型/符号

时间:2018-09-28 08:26:15

标签: pygal

我想将pygal图表中的点从默认的圆更改为矩形(听起来很奇怪,但就我而言是有意义的),并能够定义矩形的大小。我在文档中找不到解决方案。使用配置模块,我可以显示/隐藏点并更改点的大小,但据我所知我无法更改点图标。我也无法在样式模块中找到解决方案。

有简单的方法吗?

非常感谢

1 个答案:

答案 0 :(得分:0)

无法使用样式或配置来实现此目的:圆点被硬编码到呈现折线图的函数中。但是,您可以轻松地扩展折线图类并覆盖此功能以创建具有任何点形的图表。

如果您查看source code of the Line class,则会在line函数中看到以下代码:

alter(
    self.svg.transposable_node(
        dots,
        'circle',
        cx=x,
        cy=y,
        r=serie.dots_size,
        class_='dot reactive tooltip-trigger'
    ), metadata
)

这将为每个点创建一个圆并将其添加到将用于生成图表的SVG数据中。

将整个函数复制到新类中,并用以下代码替换这些行。使用dots_size配置来确定宽度和高度,这将添加正方形而不是圆形:

alter(
    self.svg.transposable_node(
        dots,
        'rect',
        x=x - serie.dots_size / 2,
        y=y - serie.dots_size / 2,
        width=serie.dots_size,
        height=serie.dots_size,
        class_='dot reactive tooltip-trigger'
    ), metadata
)

完整的类看起来像这样(看起来像很多代码,但大多数都是复制粘贴的):

from pygal.util import alter, decorate

class SquareDots(pygal.Line):

    def __init__(self, *args, **kwargs):
        super(SquareDots, self).__init__(*args, **kwargs)

    def line(self, serie, rescale=False):
        serie_node = self.svg.serie(serie)
        if rescale and self.secondary_series:
            points = self._rescale(serie.points)
        else:
            points = serie.points
        view_values = list(map(self.view, points))
        if serie.show_dots:
            for i, (x, y) in enumerate(view_values):
                if None in (x, y):
                    continue
                if self.logarithmic:
                    if points[i][1] is None or points[i][1] <= 0:
                        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_labels_major):
                    continue
                metadata = serie.metadata.get(i)
                classes = []
                if x > self.view.width / 2:
                    classes.append('left')
                if y > self.view.height / 2:
                    classes.append('top')
                classes = ' '.join(classes)
                self._confidence_interval(
                    serie_node['overlay'], x, y, serie.values[i], metadata
                )
                dots = decorate(
                    self.svg,
                    self.svg.node(serie_node['overlay'], class_="dots"),
                    metadata
                )
                val = self._format(serie, i)

                # This is the part that needs to be changed.
                alter(self.svg.transposable_node(
                        dots,
                        'rect',
                        x=x - serie.dots_size / 2,
                        y=y - serie.dots_size / 2,
                        width=serie.dots_size,
                        height=serie.dots_size,
                        class_='dot reactive tooltip-trigger'
                    ), metadata
                )

                self._tooltip_data(
                    dots, val, x, y, xlabel=self._get_x_label(i)
                )
                self._static_value(
                    serie_node, val, x + self.style.value_font_size,
                    y + self.style.value_font_size, metadata
                )
        if serie.stroke:
            if self.interpolate:
                points = serie.interpolated
                if rescale and self.secondary_series:
                    points = self._rescale(points)
                view_values = list(map(self.view, points))
            if serie.fill:
                view_values = self._fill(view_values)
            if serie.allow_interruptions:
                sequences = []
                cur_sequence = []
                for x, y in view_values:
                    if y is None and len(cur_sequence) > 0:
                        sequences.append(cur_sequence)
                        cur_sequence = []
                    elif y is None:
                        continue
                    else:
                        cur_sequence.append((x, y))
                if len(cur_sequence) > 0:
                    sequences.append(cur_sequence)
            else:
                sequences = [view_values]
            if self.logarithmic:
                for seq in sequences:
                    for ele in seq[::-1]:
                        y = points[seq.index(ele)][1]
                        if y is None or y <= 0:
                            del seq[seq.index(ele)]
            for seq in sequences:
                self.svg.line(
                    serie_node['plot'],
                    seq,
                    close=self._self_close,
                    class_='line reactive' +
                    (' nofill' if not serie.fill else '')
                )

然后您的新类就可以像其他任何pygal图表一样使用。

chart = SquareDots(dots_size=50)
chart.add("line", [1, 2, 3, 4, 3, 2])
chart.render_to_png("chart.png")

Example chart with square dots