import unittest from unittest.mock import patch from pyecharts import options as opts from pyecharts.charts import Bar, Grid, Line, Radar, Geo, Map from pyecharts.globals import ThemeType, ChartType from pyecharts.faker import Faker class TestGridComponent(unittest.TestCase): def _verify_instance_for_datazoom_and_visualmap(self, chart, key): return isinstance( chart.options.get(key), list ) and all( isinstance( item, (opts.DataZoomOpts if key == "dataZoom" else opts.VisualMapOpts, dict) ) for item in chart.options.get(key) ) def _chart_for_grid(self) -> Bar: x_data = ["{}月".format(i) for i in range(1, 13)] bar = ( Bar() .add_xaxis(x_data) .add_yaxis( "蒸发量", [2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3], yaxis_index=0, ) .add_yaxis( "降水量", [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3], yaxis_index=1, ) .extend_axis( yaxis=opts.AxisOpts(name="蒸发量", type_="value", position="right") ) .extend_axis( yaxis=opts.AxisOpts(type_="value", name="温度", position="left") ) .set_global_opts( yaxis_opts=opts.AxisOpts(name="降水量", position="right", offset=80) ) ) line = ( Line() .add_xaxis(x_data) .add_yaxis( "平均温度", [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2], yaxis_index=2, color="#675bba", label_opts=opts.LabelOpts(is_show=False), ) ) bar.overlap(line) return bar def _chart_for_set_grid_index(self, gird_index=None) -> Bar: bar = ( Bar() .add_xaxis(Faker.choose()) .add_yaxis("商家A", Faker.values()) .set_global_opts( xaxis_opts=opts.AxisOpts(grid_index=gird_index), yaxis_opts=opts.AxisOpts(grid_index=gird_index) ) ) return bar def _chart_for_grid_with_datazoom_and_visualmap(self) -> Bar: bar_1 = ( Bar() .add_xaxis(Faker.days_attrs) .add_yaxis("商家", Faker.days_values) .set_global_opts( title_opts=opts.TitleOpts(title="Bar-DataZoom"), datazoom_opts=opts.DataZoomOpts(), visualmap_opts=opts.VisualMapOpts(), toolbox_opts=opts.ToolboxOpts( feature=opts.ToolBoxFeatureOpts( save_as_image=opts.ToolBoxFeatureSaveAsImageOpts( exclude_components=["dataZoom", "toolbox"], ) ), ), legend_opts=opts.LegendOpts(), ) ) return bar_1 def _chart_for_grid_with_multiple_datazoom_and_visualmap(self) -> Bar: bar_1 = ( Bar() .add_xaxis(Faker.days_attrs) .add_yaxis("商家", Faker.days_values) .set_global_opts( title_opts=opts.TitleOpts(title="Bar-DataZoom"), datazoom_opts=[opts.DataZoomOpts(), opts.DataZoomOpts()], visualmap_opts=[opts.VisualMapOpts(), opts.VisualMapOpts()], toolbox_opts=opts.ToolboxOpts( feature=opts.ToolBoxFeatureOpts( save_as_image=opts.ToolBoxFeatureSaveAsImageOpts( exclude_components=["dataZoom", "toolbox"], ) ), ), legend_opts=opts.LegendOpts(), ) ) return bar_1 def test_grid_set_grid_index(self): bar_1 = self._chart_for_set_grid_index() bar_2 = self._chart_for_set_grid_index() bar_3 = self._chart_for_set_grid_index() gc = ( Grid() .add(chart=bar_1, grid_index=1, grid_opts=opts.GridOpts()) .add(chart=bar_2, grid_index=0, grid_opts=opts.GridOpts()) .add(chart=bar_3, grid_index=2, grid_opts=opts.GridOpts()) ) expected_idx = (1, 0, 2) for idx, series in enumerate(gc.options.get("xAxis")): self.assertEqual(series.get("gridIndex"), expected_idx[idx]) bar_1 = self._chart_for_set_grid_index() bar_2 = self._chart_for_set_grid_index() bar_3 = self._chart_for_set_grid_index() gc = ( Grid() .add(chart=bar_1, grid_index=0, grid_opts=opts.GridOpts()) .add(chart=bar_2, grid_index=2, grid_opts=opts.GridOpts()) .add(chart=bar_3, grid_index=1, grid_opts=opts.GridOpts()) ) expected_idx = (0, 2, 1) for idx, series in enumerate(gc.options.get("xAxis")): self.assertEqual(series.get("gridIndex"), expected_idx[idx]) def test_grid_set_grid_index_priority(self): bar_1 = self._chart_for_set_grid_index(gird_index=2) bar_2 = self._chart_for_set_grid_index(gird_index=0) bar_3 = self._chart_for_set_grid_index(gird_index=1) gc = ( Grid() .add(chart=bar_1, grid_index=1, grid_opts=opts.GridOpts()) .add(chart=bar_2, grid_index=2, grid_opts=opts.GridOpts()) .add(chart=bar_3, grid_index=0, grid_opts=opts.GridOpts()) ) expected_idx = (2, 0, 1) for idx, series in enumerate(gc.options.get("xAxis")): self.assertEqual(series.get("gridIndex"), expected_idx[idx]) bar_1 = self._chart_for_set_grid_index(gird_index=0) bar_2 = self._chart_for_set_grid_index(gird_index=2) bar_3 = self._chart_for_set_grid_index(gird_index=1) gc = ( Grid() .add(chart=bar_1, grid_index=2, grid_opts=opts.GridOpts()) .add(chart=bar_2, grid_index=1, grid_opts=opts.GridOpts()) .add(chart=bar_3, grid_index=0, grid_opts=opts.GridOpts()) ) expected_idx = (0, 2, 1) for idx, series in enumerate(gc.options.get("xAxis")): self.assertEqual(series.get("gridIndex"), expected_idx[idx]) def test_grid_set_grid_out_of_order(self): bar_1 = self._chart_for_set_grid_index(gird_index=2) bar_2 = self._chart_for_set_grid_index(gird_index=0) bar_3 = self._chart_for_set_grid_index(gird_index=1) gc = ( Grid() .add(chart=bar_2, grid_index=1, grid_opts=opts.GridOpts()) .add(chart=bar_3, grid_index=2, grid_opts=opts.GridOpts()) .add(chart=bar_1, grid_index=0, grid_opts=opts.GridOpts()) ) expected_idx = (0, 1, 2) for idx, series in enumerate(gc.options.get("xAxis")): self.assertEqual(series.get("gridIndex"), expected_idx[idx]) def test_grid_control_axis_index(self): bar = self._chart_for_grid() gc = Grid().add( bar, opts.GridOpts(pos_left="5%", pos_right="20%"), is_control_axis_index=True, ) expected_idx = (0, 1, 2) for idx, series in enumerate(gc.options.get("series")): self.assertEqual(series.get("yAxisIndex"), expected_idx[idx]) def test_grid_do_not_control_axis_index(self): bar = self._chart_for_grid() gc = Grid().add(bar, opts.GridOpts(pos_left="5%", pos_right="20%")) expected_idx = (0, 0, 0) for idx, series in enumerate(gc.options.get("series")): self.assertEqual(series.get("yAxisIndex"), expected_idx[idx]) def test_grid_with_more_datazoom_and_visualmap_opts(self): bar_1 = self._chart_for_grid_with_datazoom_and_visualmap() bar_2 = self._chart_for_grid_with_datazoom_and_visualmap() grid_1 = ( Grid() .add(chart=bar_1, grid_opts=opts.GridOpts()) .add(chart=bar_2, grid_opts=opts.GridOpts()) ) expected_opts_len = 2 self.assertEqual( len(grid_1.options.get("dataZoom")), expected_opts_len ) self.assertEqual( len(grid_1.options.get("visualMap")), expected_opts_len ) self.assertEqual( self._verify_instance_for_datazoom_and_visualmap(grid_1, "dataZoom"), True ) self.assertEqual( self._verify_instance_for_datazoom_and_visualmap(grid_1, "visualMap"), True ) bar_3 = self._chart_for_grid() bar_4 = self._chart_for_grid_with_datazoom_and_visualmap() grid_2 = ( Grid() .add(chart=bar_3, grid_opts=opts.GridOpts()) .add(chart=bar_4, grid_opts=opts.GridOpts()) ) expected_opts_len = 1 self.assertEqual( len(grid_2.options.get("dataZoom")), expected_opts_len ) self.assertEqual( len(grid_2.options.get("visualMap")), expected_opts_len ) self.assertEqual( self._verify_instance_for_datazoom_and_visualmap(grid_2, "dataZoom"), True ) self.assertEqual( self._verify_instance_for_datazoom_and_visualmap(grid_2, "visualMap"), True ) bar_5 = self._chart_for_grid_with_multiple_datazoom_and_visualmap() bar_6 = self._chart_for_grid_with_datazoom_and_visualmap() bar_7 = self._chart_for_grid() grid_3 = ( Grid() .add(chart=bar_5, grid_opts=opts.GridOpts()) .add(chart=bar_6, grid_opts=opts.GridOpts()) .add(chart=bar_7, grid_opts=opts.GridOpts()) ) expected_opts_len = 3 self.assertEqual( len(grid_3.options.get("dataZoom")), expected_opts_len ) self.assertEqual( len(grid_3.options.get("visualMap")), expected_opts_len ) self.assertEqual( self._verify_instance_for_datazoom_and_visualmap(grid_3, "dataZoom"), True ) self.assertEqual( self._verify_instance_for_datazoom_and_visualmap(grid_3, "visualMap"), True ) @patch("pyecharts.render.engine.write_utf8_html_file") def test_grid_options(self, fake_writer): bar = self._chart_for_grid() gc = Grid(init_opts=opts.InitOpts(theme=ThemeType.ESSOS)).add( bar, opts.GridOpts(pos_left="5%", pos_right="20%", is_contain_label=True) ) gc.render() _, content = fake_writer.call_args[0] self.assertIn("containLabel", content) self.assertIn("color", content) def _chart_for_grid_v2(self): x_data = ["{}月".format(i) for i in range(1, 13)] bar = ( Bar() .add_xaxis(x_data) .add_yaxis( "蒸发量", [2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3], yaxis_index=0, color="red", ) .add_yaxis( "降水量", [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3], yaxis_index=1, color="green", ) .extend_axis( yaxis=opts.AxisOpts(name="蒸发量", type_="value", position="right") ) .extend_axis( yaxis=opts.AxisOpts(type_="value", name="温度", position="left") ) .set_global_opts( yaxis_opts=opts.AxisOpts(name="降水量", position="right", offset=80) ) ) line = ( Line() .add_xaxis(x_data) .add_yaxis( "平均温度", [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2], yaxis_index=2, color="blue", label_opts=opts.LabelOpts(is_show=False), ) .set_global_opts(visualmap_opts=opts.VisualMapOpts()) ) bar.overlap(line) self.assertEqual(bar.colors[:3], ["red", "green", "blue"]) return bar @patch("pyecharts.render.engine.write_utf8_html_file") def test_grid_example_1(self, fake_writer): bar = self._chart_for_grid_v2() gc = Grid().add( bar, opts.GridOpts(pos_left="5%", pos_right="20%"), is_control_axis_index=True, ) expected_idx = (0, 1, 2) for idx, series in enumerate(gc.options.get("series")): self.assertEqual(series.get("yAxisIndex"), expected_idx[idx]) gc.render() _, content = fake_writer.call_args[0] self.assertIn("yAxisIndex", content) @patch("pyecharts.render.engine.write_utf8_html_file") def test_grid_geo_bar(self, fake_writer): bar = ( Bar() .add_xaxis(Faker.choose()) .add_yaxis("商家A", Faker.values()) .add_yaxis("商家B", Faker.values()) .set_global_opts( title_opts=dict(title="Bar 图"), legend_opts=opts.LegendOpts(pos_left="20%"), ) ) geo = ( Geo() .add_schema(maptype="china") .add("geo", [list(z) for z in zip(Faker.provinces, Faker.values())]) .set_series_opts(label_opts=opts.LabelOpts(is_show=False)) .set_global_opts( visualmap_opts=opts.VisualMapOpts(), title_opts=dict(title="Geo 图"), ) ) grid = ( Grid() .add(bar, grid_opts=opts.GridOpts(pos_top="50%", pos_right="75%")) .add(geo, grid_opts=opts.GridOpts(pos_left="60%")) ) grid.render() _, content = fake_writer.call_args[0] self.assertIn("geo", content) @patch("pyecharts.render.engine.write_utf8_html_file") def test_grid_two_radar(self, fake_writer): 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), ] c = ( Radar() .add_schema(schema=schema, center=["25%", "50%"]) .add( series_name="预算分配", data=[[4300, 10000, 28000, 35000, 50000, 19000]], radar_index=0, ) .set_global_opts(legend_opts=opts.LegendOpts(pos_left="20%")) ) c2 = ( Radar() .add_schema(schema=schema, center=["75%", "50%"]) .add( series_name="实际开销", data=[[5000, 14000, 28000, 31000, 42000, 21000]], radar_index=1, ) .set_global_opts(legend_opts=opts.LegendOpts(pos_right="20%")) ) grid = ( Grid().add(c, grid_opts=opts.GridOpts()).add(c2, grid_opts=opts.GridOpts()) ) grid.render() _, content = fake_writer.call_args[0] self.assertIn("radar", content) @patch("pyecharts.render.engine.write_utf8_html_file") def test_grid_mutil_yaxis(self, fake_writer): bar = ( Bar() .add_xaxis(["{}月".format(i) for i in range(1, 13)]) .add_yaxis( "蒸发量", [2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3], yaxis_index=0, color="#d14a61", ) .add_yaxis( "降水量", [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3], yaxis_index=1, color="#5793f3", ) .extend_axis( yaxis=opts.AxisOpts( name="蒸发量", type_="value", min_=0, max_=250, position="right", axisline_opts=opts.AxisLineOpts( linestyle_opts=opts.LineStyleOpts(color="#d14a61") ), axislabel_opts=opts.LabelOpts(formatter="{value} ml"), ) ) .extend_axis( yaxis=opts.AxisOpts( type_="value", name="温度", min_=0, max_=25, position="left", axisline_opts=opts.AxisLineOpts( linestyle_opts=opts.LineStyleOpts(color="#675bba") ), axislabel_opts=opts.LabelOpts(formatter="{value} °C"), splitline_opts=opts.SplitLineOpts( is_show=True, linestyle_opts=opts.LineStyleOpts(opacity=1) ), ) ) .set_global_opts( yaxis_opts=opts.AxisOpts( name="降水量", min_=0, max_=250, position="right", offset=80, axisline_opts=opts.AxisLineOpts( linestyle_opts=opts.LineStyleOpts(color="#5793f3") ), axislabel_opts=opts.LabelOpts(formatter="{value} ml"), ), title_opts=opts.TitleOpts(title="Grid-Overlap-多 X/Y 轴示例"), tooltip_opts=opts.TooltipOpts( trigger="axis", axis_pointer_type="cross" ), legend_opts=opts.LegendOpts(pos_left="25%"), ) ) line = ( Line() .add_xaxis(["{}月".format(i) for i in range(1, 13)]) .add_yaxis( "平均温度", [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2], yaxis_index=2, color="#675bba", label_opts=opts.LabelOpts(is_show=False), ) ) bar1 = ( Bar() .add_xaxis(["{}月".format(i) for i in range(1, 13)]) .add_yaxis( "蒸发量 1", [2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3], color="#d14a61", xaxis_index=1, yaxis_index=3, ) .add_yaxis( "降水量 2", [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3], color="#5793f3", xaxis_index=1, yaxis_index=3, ) .extend_axis( yaxis=opts.AxisOpts( name="蒸发量", type_="value", min_=0, max_=250, position="right", axisline_opts=opts.AxisLineOpts( linestyle_opts=opts.LineStyleOpts(color="#d14a61") ), axislabel_opts=opts.LabelOpts(formatter="{value} ml"), ) ) .extend_axis( yaxis=opts.AxisOpts( type_="value", name="温度", min_=0, max_=25, position="left", axisline_opts=opts.AxisLineOpts( linestyle_opts=opts.LineStyleOpts(color="#675bba") ), axislabel_opts=opts.LabelOpts(formatter="{value} °C"), splitline_opts=opts.SplitLineOpts( is_show=True, linestyle_opts=opts.LineStyleOpts(opacity=1) ), ) ) .set_global_opts( xaxis_opts=opts.AxisOpts(grid_index=1), yaxis_opts=opts.AxisOpts( name="降水量", min_=0, max_=250, position="right", offset=80, grid_index=1, axisline_opts=opts.AxisLineOpts( linestyle_opts=opts.LineStyleOpts(color="#5793f3") ), axislabel_opts=opts.LabelOpts(formatter="{value} ml"), ), tooltip_opts=opts.TooltipOpts( trigger="axis", axis_pointer_type="cross" ), legend_opts=opts.LegendOpts(pos_left="65%"), ) ) line1 = ( Line() .add_xaxis(["{}月".format(i) for i in range(1, 13)]) .add_yaxis( "平均温度 1", [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2], color="#675bba", label_opts=opts.LabelOpts(is_show=False), xaxis_index=1, yaxis_index=5, ) ) overlap_1 = bar.overlap(line) overlap_2 = bar1.overlap(line1) grid = ( Grid(init_opts=opts.InitOpts(width="1200px", height="800px")) .add( overlap_1, grid_opts=opts.GridOpts(pos_right="58%"), is_control_axis_index=True, ) .add( overlap_2, grid_opts=opts.GridOpts(pos_left="58%"), is_control_axis_index=True, ) ) grid.render() _, content = fake_writer.call_args[0] self.assertIn("xAxis", content) self.assertIn("yAxis", content) @patch("pyecharts.render.engine.write_utf8_html_file") def test_grid_geo_map(self, fake_writer): data_pair = [list(z) for z in zip(Faker.provinces, Faker.values())] geo_chart = ( Geo() .add_schema(maptype="china") .add( "geo", data_pair=data_pair, type_=ChartType.SCATTER, ) .set_global_opts( visualmap_opts=opts.VisualMapOpts(), ) ) map_chart = ( Map() .add("LCOH", data_pair=data_pair, maptype="china") .set_global_opts( visualmap_opts=opts.VisualMapOpts(), ) ) grid_chart = ( Grid(init_opts=opts.InitOpts()) .add(map_chart, grid_opts=opts.GridOpts()) .add(geo_chart, grid_opts=opts.GridOpts()) ) grid_chart.render() _, content = fake_writer.call_args[0] self.assertIn("geo", content)