【问题标题】:Pygal: Change dot type/ symbolPygal:更改点类型/符号
【发布时间】:2018-09-28 08:26:14
【问题描述】:

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

有什么简单的方法吗?

非常感谢

【问题讨论】:

    标签: pygal


    【解决方案1】:

    没有办法使用样式或配置来实现这一点:圆点被硬编码到呈现折线图的函数中。但是,您可以轻松扩展折线图类并重写此函数以创建具有任何点形状的图表。

    如果您查看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")
    

    【讨论】:

      猜你喜欢
      • 2019-11-30
      • 1970-01-01
      • 1970-01-01
      • 2015-08-23
      • 1970-01-01
      • 1970-01-01
      • 2013-12-03
      • 2017-08-05
      • 1970-01-01
      相关资源
      最近更新 更多