diff --git a/pyecharts/charts/basic_charts/effectscatter.py b/pyecharts/charts/basic_charts/effectscatter.py index b7242b73..fe8534fd 100644 --- a/pyecharts/charts/basic_charts/effectscatter.py +++ b/pyecharts/charts/basic_charts/effectscatter.py @@ -30,6 +30,12 @@ class EffectScatter(RectChart): ): self._append_color(color) self._append_legend(series_name, is_selected) + + if all([isinstance(d, opts.EffectScatterItem) for d in y_axis]): + y_axis = y_axis + else: + y_axis = [list(z) for z in zip(self._xaxis_data, y_axis)] + self.options.get("series").append( { "type": ChartType.EFFECT_SCATTER, @@ -41,7 +47,7 @@ class EffectScatter(RectChart): "symbol": symbol, "symbolSize": symbol_size, "symbolRotate": symbol_rotate, - "data": [list(z) for z in zip(self._xaxis_data, y_axis)], + "data": y_axis, "label": label_opts, "tooltip": tooltip_opts, "itemStyle": itemstyle_opts, diff --git a/pyecharts/charts/basic_charts/funnel.py b/pyecharts/charts/basic_charts/funnel.py index e023ba10..af191012 100644 --- a/pyecharts/charts/basic_charts/funnel.py +++ b/pyecharts/charts/basic_charts/funnel.py @@ -29,9 +29,14 @@ class Funnel(Chart): itemstyle_opts: types.ItemStyle = None, ): self._append_color(color) - data = [{"name": n, "value": v} for n, v in data_pair] - for a, _ in data_pair: - self._append_legend(a, is_selected) + if all([isinstance(d, opts.FunnelItem) for d in data_pair]): + data = data_pair + for a in data_pair: + self._append_legend(a.opts.get("name"), is_selected) + else: + data = [{"name": n, "value": v} for n, v in data_pair] + for a, _ in data_pair: + self._append_legend(a, is_selected) _dset = set(self.options.get("legend")[0].get("data")) self.options.get("legend")[0].update(data=list(_dset)) diff --git a/pyecharts/charts/basic_charts/heatmap.py b/pyecharts/charts/basic_charts/heatmap.py index b6a110bc..9d12f399 100644 --- a/pyecharts/charts/basic_charts/heatmap.py +++ b/pyecharts/charts/basic_charts/heatmap.py @@ -20,8 +20,8 @@ class HeatMap(RectChart): def add_yaxis( self, series_name: str, - yaxis_data: types.Sequence[types.Union[opts.HeatMapItem, dict]], - value: types.Sequence[types.Union[opts.HeatMapItem, dict]], + yaxis_data: types.Sequence[types.Union[dict]], + value: types.Sequence[types.Union[dict]], *, is_selected: bool = True, xaxis_index: types.Optional[types.Numeric] = None, diff --git a/pyecharts/charts/basic_charts/line.py b/pyecharts/charts/basic_charts/line.py index 248dcf0d..011b1b15 100644 --- a/pyecharts/charts/basic_charts/line.py +++ b/pyecharts/charts/basic_charts/line.py @@ -42,9 +42,13 @@ class Line(RectChart): ): self._append_color(color) self._append_legend(series_name, is_selected) - # 合并 x 和 y 轴数据,避免当 X 轴的类型设置为 'value' 的时候, - # X、Y 轴均显示 Y 轴数据 - data = [list(z) for z in zip(self._xaxis_data, y_axis)] + + if all([isinstance(d, opts.LineItem) for d in y_axis]): + data = y_axis + else: + # 合并 x 和 y 轴数据,避免当 X 轴的类型设置为 'value' 的时候, + # X、Y 轴均显示 Y 轴数据 + data = [list(z) for z in zip(self._xaxis_data, y_axis)] self.options.get("series").append( { diff --git a/pyecharts/charts/basic_charts/parallel.py b/pyecharts/charts/basic_charts/parallel.py index 98cffc17..8a2f7311 100644 --- a/pyecharts/charts/basic_charts/parallel.py +++ b/pyecharts/charts/basic_charts/parallel.py @@ -37,7 +37,7 @@ class Parallel(Chart): def add( self, series_name: str, - data: types.Sequence[types.Union[opts.ParallelItem, dict]], + data: types.Sequence[types.Union[dict]], *, is_smooth: bool = False, is_selected: bool = True, diff --git a/pyecharts/charts/basic_charts/radar.py b/pyecharts/charts/basic_charts/radar.py index b1d0c5d1..e687fa1a 100644 --- a/pyecharts/charts/basic_charts/radar.py +++ b/pyecharts/charts/basic_charts/radar.py @@ -63,7 +63,11 @@ class Radar(Chart): areastyle_opts: opts.AreaStyleOpts = opts.AreaStyleOpts(), tooltip_opts: types.Tooltip = None, ): - self._append_legend(series_name, is_selected) + if all([isinstance(d, opts.RadarItem) for d in data]): + for a in data: + self._append_legend(a.get("name"), is_selected) + else: + self._append_legend(series_name, is_selected) self.options.get("series").append( { "type": ChartType.RADAR, diff --git a/pyecharts/commons/utils.py b/pyecharts/commons/utils.py index 78c0b1c5..2da93772 100644 --- a/pyecharts/commons/utils.py +++ b/pyecharts/commons/utils.py @@ -81,7 +81,7 @@ def _clean_dict(mydict): # delete key with empty string continue - yield (key, value) + yield key, value def _clean_array(myarray): diff --git a/pyecharts/options/__init__.py b/pyecharts/options/__init__.py index 4bbaf231..dd58ccf6 100644 --- a/pyecharts/options/__init__.py +++ b/pyecharts/options/__init__.py @@ -13,6 +13,7 @@ from .charts_options import ( CandleStickItem, ComponentTitleOpts, EffectScatterItem, + FunnelItem, GaugeDetailOpts, GaugePointerOpts, GaugeTitleOpts, @@ -28,7 +29,6 @@ from .charts_options import ( GraphicTextStyleOpts, GraphLink, GraphNode, - HeatMapItem, LineItem, MapItem, Map3DColorMaterialOpts, @@ -39,7 +39,6 @@ from .charts_options import ( Map3DRealisticMaterialOpts, Map3DViewControlOpts, PageLayoutOpts, - ParallelItem, PieItem, PieLabelLineOpts, RadarItem, diff --git a/pyecharts/options/charts_options.py b/pyecharts/options/charts_options.py index 44a32ff0..5de90a83 100644 --- a/pyecharts/options/charts_options.py +++ b/pyecharts/options/charts_options.py @@ -21,8 +21,9 @@ from .series_options import ( class BarItem(BasicOpts): def __init__( self, - name: Optional[str] = None, - value: Optional[Numeric] = None, + name: Union[int, str], + value: Numeric, + *, label_opts: Union[LabelOpts, dict, None] = None, itemstyle_opts: Union[ItemStyleOpts, dict, None] = None, tooltip_opts: Union[TooltipOpts, dict, None] = None, @@ -39,8 +40,9 @@ class BarItem(BasicOpts): class BoxplotItem(BasicOpts): def __init__( self, - name: Optional[str] = None, - value: Optional[Sequence] = None, + name: Union[int, str], + value: Sequence, + *, itemstyle_opts: Union[ItemStyleOpts, dict, None] = None, tooltip_opts: Union[TooltipOpts, dict, None] = None, ): @@ -55,8 +57,9 @@ class BoxplotItem(BasicOpts): class CandleStickItem(BasicOpts): def __init__( self, - name: Optional[str] = None, - value: Optional[Sequence] = None, + name: Union[str, int], + value: Sequence, + *, itemstyle_opts: Union[ItemStyleOpts, dict, None] = None, tooltip_opts: Union[TooltipOpts, dict, None] = None, ): @@ -71,8 +74,9 @@ class CandleStickItem(BasicOpts): class EffectScatterItem(BasicOpts): def __init__( self, - name: Union[str, Numeric] = None, - value: Union[str, Numeric] = None, + name: Union[str, Numeric], + value: Union[str, Numeric], + *, symbol: Optional[str] = None, symbol_size: Union[Sequence[Numeric], Numeric] = None, symbol_rotate: Optional[Numeric] = None, @@ -96,17 +100,28 @@ class EffectScatterItem(BasicOpts): } -class HeatMapItem(BasicOpts): +class FunnelItem(BasicOpts): def __init__( self, - name: Optional[str] = None, - value: Optional[Sequence] = None, + name: Union[str, int], + value: Union[Sequence, str, Numeric], + *, + is_show_label_line: Optional[bool] = None, + label_line_width: Optional[int] = None, + label_line_linestyle_opts: Union[LineStyleOpts, dict, None] = None, + label_opts: Union[LabelOpts, dict, None] = None, itemstyle_opts: Union[ItemStyleOpts, dict, None] = None, tooltip_opts: Union[TooltipOpts, dict, None] = None, ): self.opts: dict = { "name": name, "value": value, + "labelLine": { + "show": is_show_label_line, + "length": label_line_width, + "lineStyle": label_line_linestyle_opts, + }, + "label": label_opts, "itemStyle": itemstyle_opts, "tooltip": tooltip_opts, } @@ -117,6 +132,7 @@ class LineItem(BasicOpts): self, name: Union[str, Numeric] = None, value: Union[str, Numeric] = None, + *, symbol: Optional[str] = "circle", symbol_size: Numeric = 4, symbol_rotate: Optional[Numeric] = None, @@ -144,7 +160,7 @@ class MapItem(BasicOpts): def __init__( self, name: Optional[str] = None, - value: Optional[Numeric] = None, + value: Union[Sequence, Numeric, str] = None, is_selected: bool = False, label_opts: Union[LabelOpts, dict, None] = None, itemstyle_opts: Union[ItemStyleOpts, dict, None] = None, @@ -160,28 +176,6 @@ class MapItem(BasicOpts): } -class ParallelItem(BasicOpts): - def __init__( - self, - name: Optional[str] = None, - value: Optional[Sequence] = None, - linestyle_opts: Union[LineStyleOpts, dict, None] = None, - color: Union[str, dict] = "#000", - width: Numeric = 2, - type_: str = "solid", - opacity: Numeric = 0.45, - ): - self.opts: dict = { - "name": name, - "value": value, - "lineStyle": linestyle_opts, - "color": color, - "width": width, - "type": type_, - "opacity": opacity, - } - - class PieItem(BasicOpts): def __init__( self, @@ -206,7 +200,7 @@ class RadarItem(BasicOpts): def __init__( self, name: Optional[str] = None, - value: Optional[Numeric] = None, + value: Union[Sequence, Numeric, str] = None, symbol: Optional[str] = None, symbol_size: Union[Sequence[Numeric], Numeric] = None, symbol_rotate: Optional[Numeric] = None, diff --git a/test/test_bar.py b/test/test_bar.py index 1cd25288..fd351ada 100644 --- a/test/test_bar.py +++ b/test/test_bar.py @@ -28,6 +28,29 @@ def test_bar_base(fake_writer): assert_equal(c.renderer, "canvas") +@patch("pyecharts.render.engine.write_utf8_html_file") +def test_bar_item_base(fake_writer): + x_axis = ["A", "B", "C"] + bar_item_1 = [ + opts.BarItem(name=d[0], value=d[1]) for d in list(zip(x_axis, [1, 2, 3])) + ] + bar_item_2 = [ + opts.BarItem(name=d[0], value=d[1]) for d in list(zip(x_axis, [4, 5, 6])) + ] + + c = ( + Bar() + .add_xaxis(x_axis) + .add_yaxis("series0", bar_item_1) + .add_yaxis("series1", bar_item_2) + ) + c.render() + _, content = fake_writer.call_args[0] + assert_greater(len(content), 2000) + assert_equal(c.theme, "white") + assert_equal(c.renderer, "canvas") + + @patch("pyecharts.render.engine.write_utf8_html_file") def test_bar_base_with_animation(fake_writer): c = ( diff --git a/test/test_boxplot.py b/test/test_boxplot.py index aa9d7f01..31fe5e1a 100644 --- a/test/test_boxplot.py +++ b/test/test_boxplot.py @@ -2,11 +2,12 @@ from unittest.mock import patch from nose.tools import assert_equal +from pyecharts import options as opts from pyecharts.charts import Boxplot @patch("pyecharts.render.engine.write_utf8_html_file") -def test_boxpolt_base(fake_writer): +def test_boxplot_base(fake_writer): v1 = [ [850, 740, 900, 1070, 930, 850, 950, 980, 980, 880, 1000, 980], [960, 940, 960, 940, 880, 800, 850, 880, 900, 840, 830, 790], @@ -23,3 +24,27 @@ def test_boxpolt_base(fake_writer): _, content = fake_writer.call_args[0] assert_equal(c.theme, "white") assert_equal(c.renderer, "canvas") + + +@patch("pyecharts.render.engine.write_utf8_html_file") +def test_boxplot_item_base(fake_writer): + x_axis = ["expr1", "expr2"] + v1 = [ + [850, 740, 900, 1070, 930, 850, 950, 980, 980, 880, 1000, 980], + [960, 940, 960, 940, 880, 800, 850, 880, 900, 840, 830, 790], + ] + v2 = [ + [890, 810, 810, 820, 800, 770, 760, 740, 750, 760, 910, 920], + [890, 840, 780, 810, 760, 810, 790, 810, 820, 850, 870, 870], + ] + c = Boxplot() + + series_a = [opts.BoxplotItem(name=x_axis[0], value=d) for d in c.prepare_data(v1)] + series_b = [opts.BoxplotItem(name=x_axis[1], value=d) for d in c.prepare_data(v2)] + + c.add_xaxis(xaxis_data=x_axis).add_yaxis("A", series_a).add_yaxis( + "B", series_b) + c.render() + _, content = fake_writer.call_args[0] + assert_equal(c.theme, "white") + assert_equal(c.renderer, "canvas") diff --git a/test/test_effectscatter.py b/test/test_effectscatter.py index 59b4605b..b95263cf 100644 --- a/test/test_effectscatter.py +++ b/test/test_effectscatter.py @@ -2,6 +2,7 @@ from unittest.mock import patch from nose.tools import assert_equal +from pyecharts import options as opts from pyecharts.charts import EffectScatter from pyecharts.faker import Faker @@ -13,3 +14,18 @@ def test_effectscatter_base(fake_writer): _, content = fake_writer.call_args[0] assert_equal(c.theme, "white") assert_equal(c.renderer, "canvas") + + +@patch("pyecharts.render.engine.write_utf8_html_file") +def test_effectscatter_item_base(fake_writer): + x_axis = Faker.choose() + chart_item = [ + opts.EffectScatterItem(name=d[0], value=d[1]) + for d in list(zip(x_axis, Faker.values())) + ] + + c = EffectScatter().add_xaxis(x_axis).add_yaxis("", chart_item) + c.render() + _, content = fake_writer.call_args[0] + assert_equal(c.theme, "white") + assert_equal(c.renderer, "canvas") diff --git a/test/test_funnel.py b/test/test_funnel.py index 66feec65..1ac89504 100644 --- a/test/test_funnel.py +++ b/test/test_funnel.py @@ -2,6 +2,7 @@ from unittest.mock import patch from nose.tools import assert_equal +from pyecharts import options as opts from pyecharts.charts import Funnel from pyecharts.faker import Faker @@ -13,3 +14,20 @@ def test_funnel_base(fake_writer): _, content = fake_writer.call_args[0] assert_equal(c.theme, "white") assert_equal(c.renderer, "canvas") + + +@patch("pyecharts.render.engine.write_utf8_html_file") +def test_funnel_item_base(fake_writer): + funnel_item = [ + opts.FunnelItem(name=d[0], value=d[1]) + for d in list(zip(Faker.choose(), Faker.values())) + ] + c = ( + Funnel() + .add("商品", funnel_item) + .set_global_opts(title_opts=opts.TitleOpts(title="Funnel-基本示例")) + ) + c.render() + _, content = fake_writer.call_args[0] + assert_equal(c.theme, "white") + assert_equal(c.renderer, "canvas") diff --git a/test/test_kline.py b/test/test_kline.py index e5cd2cb3..92bc86da 100644 --- a/test/test_kline.py +++ b/test/test_kline.py @@ -36,6 +36,30 @@ def test_kline_base(fake_writer): assert_equal(c.renderer, "canvas") +@patch("pyecharts.render.engine.write_utf8_html_file") +def test_kline_item_base(fake_writer): + x_axis = ["2017/7/{}".format(i + 1) for i in range(10)] + y_axis = data + kline_item = [ + opts.CandleStickItem(name=d[0], value=d[1]) + for d in list(zip(x_axis, y_axis)) + ] + + c = ( + Kline() + .add_xaxis(x_axis) + .add_yaxis("kline", kline_item) + .set_global_opts( + yaxis_opts=opts.AxisOpts(is_scale=True), + xaxis_opts=opts.AxisOpts(is_scale=True), + ) + ) + c.render() + _, content = fake_writer.call_args[0] + assert_equal(c.theme, "white") + assert_equal(c.renderer, "canvas") + + @patch("pyecharts.render.engine.write_utf8_html_file") def test_kline_axispointer_opts(fake_writer): c = ( diff --git a/test/test_line.py b/test/test_line.py index a35621d4..fe07b450 100644 --- a/test/test_line.py +++ b/test/test_line.py @@ -20,6 +20,30 @@ def test_bar_base(fake_writer): assert_equal(c.renderer, "canvas") +@patch("pyecharts.render.engine.write_utf8_html_file") +def test_bar_item_base(fake_writer): + x_axis = ["A", "B", "C"] + y_axis_0 = [1, 2, 4] + line_item_0 = [ + opts.LineItem(name=d[0], value=d[1]) for d in list(zip(x_axis, y_axis_0)) + ] + y_axis_1 = [2, 3, 6] + line_item_1 = [ + opts.LineItem(name=d[0], value=d[1]) for d in list(zip(x_axis, y_axis_1)) + ] + + c = ( + Line() + .add_xaxis(x_axis) + .add_yaxis("series0", line_item_0) + .add_yaxis("series1", line_item_1) + ) + c.render() + _, content = fake_writer.call_args[0] + assert_equal(c.theme, "white") + assert_equal(c.renderer, "canvas") + + @patch("pyecharts.render.engine.write_utf8_html_file") def test_set_global_opts(fake_writer): test_type, test_range = "test_type", [-10001, 10001] diff --git a/test/test_map.py b/test/test_map.py index 0ce38e9c..7a0a4018 100644 --- a/test/test_map.py +++ b/test/test_map.py @@ -19,6 +19,22 @@ def test_map_base(fake_writer): assert_equal(c.renderer, "canvas") +@patch("pyecharts.render.engine.write_utf8_html_file") +def test_map_item_base(fake_writer): + location_name = ["广东"] + location_data = [[100, 200, 300, 400]] + mock_data = [ + opts.MapItem(name=d[0], value=d[1]) + for d in list(zip(location_name, location_data)) + ] + + c = Map().add("商家A", mock_data, "china") + c.render() + _, content = fake_writer.call_args[0] + assert_equal(c.theme, "white") + assert_equal(c.renderer, "canvas") + + def test_map_emphasis(): c = Map().add( "商家A", diff --git a/test/test_pie.py b/test/test_pie.py index 3b87cd0f..0120ed80 100644 --- a/test/test_pie.py +++ b/test/test_pie.py @@ -18,3 +18,22 @@ def test_pie_base(fake_writer): _, content = fake_writer.call_args[0] assert_equal(c.theme, "white") assert_equal(c.renderer, "canvas") + + +@patch("pyecharts.render.engine.write_utf8_html_file") +def test_pie_item_base(fake_writer): + d = [ + opts.PieItem(name="河马", value=131), + opts.PieItem(name="蟒蛇", value=89), + opts.PieItem(name="老虎", value=149), + opts.PieItem(name="大象", value=178), + ] + c = ( + Pie() + .add("", d) + .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}")) + ) + c.render() + _, content = fake_writer.call_args[0] + assert_equal(c.theme, "white") + assert_equal(c.renderer, "canvas") diff --git a/test/test_radar.py b/test/test_radar.py index 2736ffe3..1adbbc85 100644 --- a/test/test_radar.py +++ b/test/test_radar.py @@ -33,6 +33,40 @@ def test_radar_base(fake_writer): assert_equal(c.renderer, "canvas") +@patch("pyecharts.render.engine.write_utf8_html_file") +def test_radar_item_base(fake_writer): + series_names = ["预算分配", "实际开销"] + series_data = [ + [4300, 10000, 28000, 35000, 50000, 19000], + [5000, 14000, 28000, 31000, 42000, 21000], + ] + radar_item = [ + opts.RadarItem(name=d[0], value=d[1]) + for d in list(zip(series_names, series_data)) + ] + + c = ( + Radar() + .add_schema( + schema=[ + opts.RadarIndicatorItem(name="销售", max_=6500), + opts.RadarIndicatorItem(name="管理", max_=16000), + opts.RadarIndicatorItem(name="信息技术", max_=30000), + opts.RadarIndicatorItem(name="客服", max_=38000), + opts.RadarIndicatorItem(name="研发", max_=52000), + opts.RadarIndicatorItem(name="市场", max_=25000), + ] + ) + .add("", radar_item) + .set_series_opts(label_opts=opts.LabelOpts(is_show=False)) + .set_global_opts(title_opts=opts.TitleOpts(title="Radar-基本示例")) + ) + c.render() + _, content = fake_writer.call_args[0] + assert_equal(c.theme, "white") + assert_equal(c.renderer, "canvas") + + @patch("pyecharts.render.engine.write_utf8_html_file") def test_radar_options(fake_writer): c = ( diff --git a/test/test_scatter.py b/test/test_scatter.py index 2a365fb9..166d1fc4 100644 --- a/test/test_scatter.py +++ b/test/test_scatter.py @@ -2,11 +2,12 @@ from unittest.mock import patch from nose.tools import assert_equal +from pyecharts import options as opts from pyecharts.charts import Scatter @patch("pyecharts.render.engine.write_utf8_html_file") -def test_bar_base(fake_writer): +def test_scatter_base(fake_writer): c = ( Scatter() .add_xaxis(["A", "B", "C"]) @@ -17,3 +18,23 @@ def test_bar_base(fake_writer): _, content = fake_writer.call_args[0] assert_equal(c.theme, "white") assert_equal(c.renderer, "canvas") + + +@patch("pyecharts.render.engine.write_utf8_html_file") +def test_scatter_item_base(fake_writer): + x_axis = ["A", "B", "C"] + y_axis = [1, 2, 4] + chart_item = [ + opts.ScatterItem(name=d[0], value=d[1]) for d in list(zip(x_axis, y_axis)) + ] + + c = ( + Scatter() + .add_xaxis(x_axis) + .add_yaxis("series0", chart_item) + .set_global_opts(title_opts=opts.TitleOpts(title="Scatter-基本示例")) + ) + c.render() + _, content = fake_writer.call_args[0] + assert_equal(c.theme, "white") + assert_equal(c.renderer, "canvas")