Chapter 28. Chart2 API Overview

At over 1,600 pages the OpenOffice Developer’s Guide isn’t a quick read, but you might expect it to cover all the major parts of the Office API. That’s mostly true, except for one omission - there’s no mention of the chart2 charting module. It’s absent from the guide, the online examples, and only makes a brief appearance in the Wiki, at https://wiki.openoffice.org/wiki/Chart2.

That’s not to say that chart creation isn’t explained in the guide; chapter 10 is all about that topic, but using the older charting module, called chart (as you might guess). One source of confusion is that both modules (chart and chart2) have a similar top-level interface to Calc via XTableChart and XChartDocument, but they rapidly diverge as you progress further into the APIs. A sure way to make code crash is to mix services and interfaces from the two modules.

Since newer is obviously better, the question arises as to why the Developer’s Guide skips chart2? The reason seems to be historical - the guide was written for OpenOffice version 3.1, which dates from the middle of 2009. The chart2 module was released two years before, in September 2007, for version 2.3. That release came with dire warnings in the Wiki about the API being unstable and subject to change. I’m sure those warnings were valid back in 2007, but chart2 underwent a lot of development over the next three years before LibreOffice was forked off in September 2010. After that the pace of change slowed, mainly because the module was stable. For example, Calc’s charting wizard is implemented using chart2.

Since the developer’s guide hasn’t been updated in since 2011, the chart2 module hasn’t received much notice. OooDev is rectifying that by concentrating solely on chart2 programming; The old chart API is not used here, although OooDev has a Chart class.

The primary source of online information about chart2 is its API documentation.

One problem with searching the Web for examples is that programs using the chart2 and chart modules look similar. The crucial difference for Python is that most of the chart2 services and interfaces are inside the com.sun.star.chart2 package whereas the older chart services and interfaces are inside com.sun.star.chart.

Another way to distinguish between examples, especially for programs written in Basic, is to look at the names of the chart services. The old chart names end with the word Diagram (i.e. BarDiagram, DonutDiagram, LineDiagram) whereas the chart2 names either end with the word ChartType (i.e. BarChartType, PieChartType, LineChartType), or with no special word (i.e. Bar, Donut, Line).

A good way to get a feel for chart2's functionality is to look at chapter 3 of the Calc User Guide, available from https://libreoffice.org/get-help/documentation. It describes the charting wizard, which acts as a very nice GUI for most of the chart2 API. The chapter also introduces a lot of charting terminology (i.e. chart types, data ranges, data series) used in the API.

28.1 Charting Elements

Different chart types share many common elements, as illustrated in Fig. 227.

Typical Chart Elements.

Fig. 227 :Typical Chart Elements.

Most of the labeled components in Fig. 227 are automatically included when a chart template is instantiated; the programmer typically only has to supply text and a few property settings, such as the wall color and font size.

There are ten main chart types, which are listed when the “Chart Wizard” is started via Calc’s Insert, Chart menu item. Fig. 228 shows the possibilities.

Ten Chart Types

Fig. 228 :Ten Chart Types.

Most of the types offer variants, which are displayed as icons to the right of the dialog window. When you position the mouse over an icon, the name of the variant appears briefly in a tooltip, as in Fig. 229.

A Column Chart Icon with its Tooltip Name

Fig. 229 :A Column Chart Icon with its Tooltip Name.

When the checkboxes, buttons, or combo boxes are selected in the dialog, the icons change to reflect changes in the variants.

The three most common variants are 3D, Stacked and Percent. Stacked is utilized when the chart displays multiple data sequences stacked on top of each other. Percent is combined with Stacked to stack the sequences in terms of their percentage contribution to the total. A lengthy discussion about chart variants can be found in chapter 3 of the Calc User Guide, in the section Gallery of Chart Types.

In the chart2 API, the variants are accessed via template names, which are listed in Table 8.

Table 8 Chart Types and Template Names

Chart

Types

Template Names

Column

Stacked

  • Column

  • StackedColumn

  • PercentStackedColumn

Percent

  • ThreeDColumnDeep

  • ThreeDColumnFlat

3D

  • StackedThreeDColumnFlat

  • PercentStackedThreeDColumnFlat

Bar

Stacked

  • Bar

  • StackedBar

  • PercentStackedBar

Percent

  • ThreeDBarDeep

  • ThreeDBarFlat

3D

  • StackedThreeDBarFlat

  • PercentStackedThreeDBarFlat

Pie

Donut

  • Pie

  • Donut

Explode

  • PieAllExploded

  • DonutAllExploded

3D

  • ThreeDPie

  • ThreeDPieAllExploded

  • ThreeDDonut

  • ThreeDDonutAllExploded

Area

Stacked

  • Area

  • StackedArea

  • PercentStackedArea

Percent

  • ThreeDArea

  • StackedThreeDArea

3D

  • PercentStackedThreeDArea

Line

Symbol

  • Line

  • Symbol

  • LineSymbol

Stacked

  • StackedLine

  • StackedSymbol

  • StackedLineSymbol

Percent

  • PercentStackedLine

  • PercentStackedSymbol

3D

  • PercentStackedLineSymbol

  • ThreeDLine

  • ThreeDLineDeep

  • StackedThreeDLine

  • PercentStackedThreeDLine

XY

Line

  • ScatterSymbol

  • ScatterLine

  • ScatterLineSymbol

(Scatter)

3D

  • ThreeDScatter

Bubble

  • Bubble

Net

Line

  • Net

  • NetLine

  • NetSymbol

  • FilledNet

Symbol

  • StackedNet

  • StackedNetLine

Filled

  • StackedNetSymbol

  • StackedFilledNet

Stacked

  • PercentStackedNet

  • PercentStackedNetLine

  • PercentStackedNetSymbol

Percent

  • PercentStackedFilledNet

Stock

Open

  • StockLowHighClose

Volume

  • StockOpenLowHighClose

  • StockVolumeLowHighClose

  • StockVolumeOpenLowHighClose

Column and Line

Stacked

  • ColumnWithLine

  • StackedColumnWithLine

The template names are closely related to the tooltip names in Calc’s chart wizard. For example, the tooltip name in Fig. 229 corresponds to the PercentStackedColumn template.

It’s also possible to create a chart using a chart type name, which are listed in Table 9.

Table 9 Chart Type Names.

Chart

Chart Type Names

Column

ColumnChartType

Bar

BarChartType

Pie

PieChartType

Area

AreaChartType

Line

LineChartType

XY

(Scatter) ScatterChartType

Bubble

BubbleChartType

Net

NetChartType, FilledNetChartType

Stock

CandleStickChartType

OooDev has ChartTypes class for looking up chart names to make it a bit easier for a developer. ChartTypes is has a sub-class for each chart type shown in Table 9. Each sub-class has a NAMED field which contain the name in column 2 of Table 9. Also each sub-class has one or more fields that start with TEMPLATE_ such as TEMPLATE_3D or TEMPLATE_PERCENT. TEMPLATE_ fields point to the possible chart template names listed in column 3 of Table 8.

For Example diagram_name of Chart2.insert_chart() can be passed ChartTypes.Pie.TEMPLATE_DONUT.DONUT.

range_addr = Calc.get_address(sheet=sheet, range_name="A44:C50")
chart_doc = Chart2.insert_chart(
    sheet=sheet,
    cells_range=range_addr,
    cell_name="D43",
    width=15,
    height=11,
    diagram_name=ChartTypes.Pie.TEMPLATE_DONUT.DONUT,
)

Note that a stock chart graph is drawn using a CandleStickChartType, and that there’s no type name for a column and line chart because it’s implemented as a combination of ColumnChartType and BarChartType.

The chart2 module is quite complex, so OooDev hides a lot of details inside methods in Chart2 class. It simplifies four kinds of operation:

  1. The creation of a new chart in a spreadsheet document, based on a template name.

  2. The accessing and modification of elements inside a chart, such as the title, legend, axes, and colors.

  3. The addition of extra data to a chart, such as error bars or a second graph.

  4. The embedding of a chart in a document other than a spreadsheet, namely in a text document or slide presentation.

Operations no. 1 (chart creation) and no. 2 (element modification) are used by all my examples, so the rest of this chapter will give an overview of how the corresponding Chart2 methods work.

Programming details specific to particular charts will be discussed in subsequent chapters:

  • column: chapter 29;

  • bar, pie, area, line: chapter 30;

  • XY (scatter): chapter 31;

  • bubble, net, stock: chapter 32.

28.2 Chart Creation

Chart creation can be divided into three steps:

  1. A TableChart service is created inside the spreadsheet.

  2. The ChartDocument service is accessed inside the TableChart.

  3. The ChartDocument is initialized by linking together a chart template, diagram, and data source.

The details are explained in the following sub-sections.

28.2.1 Creating a Table Chart

XTableCharts.addNewByName() adds a new TableChart to the TableCharts collection in a spreadsheet. This is shown graphically in Fig. 230, and is implemented by Chart2.add_table_chart().

Creating a new TableChart Service

Fig. 230 :Creating a new TableChart Service.

Chart2.add_table_chart() is defined as:

# in Chart2 class
@staticmethod
def add_table_chart(
    sheet: XSpreadsheet, chart_name: str, cells_range: CellRangeAddress,
    cell_name: str, width: int, height: int
) -> None:
    try:
        charts_supp = Lo.qi(XTableChartsSupplier, sheet, True)
        tbl_charts = charts_supp.getCharts()

        pos = Calc.get_cell_pos(sheet, cell_name)
        rect = Rectangle(X=pos.X, Y=pos.Y, Width=width * 1_000, Height=height * 1_000)
        addrs = (cells_range,)

        tbl_charts.addNewByName(chart_name, rect, addrs, True, True)
    except Exception as e:
        raise ChartError("Error adding table chart") from e

The arguments passed to Chart2.add_table_chart() include the new chart’s name, the cell range used as a data source, and the chart’s position and dimensions when drawn in the Calc window.

The position is a cell name (i.e. A1), which becomes the location of the top-left corner of the chart in the Calc window. The name is converted into a position by calling Calc.get_cell_pos(). The size of the chart is supplied as millimeter width and height arguments and converted into a Rectangle in 1/100mm units.

The methods assume that the data range has a specific format, which is illustrated by Fig. 231.

Cell Range Data Format

Fig. 231 :Cell Range Data Format.

The data is organized into columns, the first for the x-axis categories, and the others for the y-axis data displayed as graphs. The first row of the data range contains labels for the x-axis and the graphs.

For example, the data range in Fig. 231 is drawn as a Column chart in Fig. 232.

A Column Chart Using the Data in previous figure.

Fig. 232 :A Column Chart Using the Data in Fig. 231.

The assumption that the first data column are x-axis categories doesn’t apply to scatter and bubble charts which use numerical x-axis values. There are examples of those in later chapters.

The data format assumptions are used in the call to XTableCharts.addNewByName() in Chart2.add_table_chart() by setting its last two arguments to True. This specifies that the top row and left column will be used as categories and/or labels. More specific format information will be supplied later.

28.2.2 Accessing the Chart Document

Although Chart2.add_table_chart() adds a table chart to the spreadsheet, it doesn’t return a reference to the new chart document. That’s obtained by calling Chart2.get_chart_doc():

Chart2.add_table_chart(
    sheet=sheet,
    chart_name=chart_name,
    cells_range=cells_range,
    cell_name=cell_name,
    width=width,
    height=height
)
chartDoc = Chart2.get_chart_doc(sheet=sheet, chart_name=chartName) # XChartDocument

Chart2.get_chart_doc() accesses the spreadsheet’s collection of TableCharts, searching for the one with the given name. The matching TableChart service is treated as an XEmbeddedObjectSupplier interface, which lets its embedded chart document be referenced. These steps are illustrated by Fig. 233.

Accessing a Chart Document.

Fig. 233 :Accessing a Chart Document.

Chart2.get_chart_doc() implements Fig. 233, using Chart2.get_table_chart() to access the named table chart:

# in Chart2 class
@classmethod
def get_chart_doc(cls, sheet: XSpreadsheet, chart_name: str) -> XChartDocument:
    try:
        tbl_chart = cls.get_table_chart(sheet, chart_name)
        eos = Lo.qi(XEmbeddedObjectSupplier, tbl_chart, True)
        return Lo.qi(XChartDocument, eos.getEmbeddedObject(), True)
    except ChartError:
        raise
    except Exception as e:
        raise ChartError(f'Error getting chart document for chart "{chart_name}"') from e

@staticmethod
def get_table_chart(sheet: XSpreadsheet, chart_name: str) -> XTableChart:
    try:
        charts_supp = Lo.qi(XTableChartsSupplier, sheet, True)
        tbl_charts = charts_supp.getCharts()
        tc_access = Lo.qi(XNameAccess, tbl_charts, True)
        tbl_chart = Lo.qi(XTableChart, tc_access.getByName(chart_name))
        return tbl_chart
    except Exception as e:
        raise ChartError(f'Error getting table chart for chart "{chart_name}"') from e

28.2.3 Initializing the Chart Document

The chart document is initialized by linking three components: the chart template, the chart’s diagram, and a data source, as illustrated by Fig. 234.

Initializing a Chart Document

Fig. 234 :Initializing a Chart Document.

The initialization steps in Fig. 234, and the earlier calls to Chart2.add_table_chart() and Chart2.get_chart_doc() are carried out by Chart2.insert_chart(). A typical call to insert_chart() would be:

range_addr = Calc.get_address(sheet=sheet, range_name="E15:G21") # CellRangeAddress
chart_doc =  Chart2.insert_chart(
    sheet=sheet,
    cells_range=range_addr,
    cell_name="A22",
    width=20,
    height=11,
    diagram_name=ChartTypes.Column.TEMPLATE_STACKED.COLUMN # or "Column"
) # XChartDocument

The first line converts E15:G21 into a data range (this corresponds to the cells shown in Fig. 231), which is passed to Chart2.insert_chart(). The A22 string and the 20x11 mm dimensions specify the position and size of the chart, and the last argument (Column) is the desired chart template (see Table 8, see ChartTypes). The result is the column chart shown in Fig. 232.

Chart2.insert_chart() is:

# in Chart2 class
@classmethod
def insert_chart(
    cls,
    sheet: XSpreadsheet,
    cells_range: CellRangeAddress,
    cell_name: str,
    width: int,
    height: int,
    diagram_name: ChartTemplateBase | str,
    color_bg: Color = mColor.CommonColor.PALE_BLUE,
    color_wall: Color = mColor.CommonColor.LIGHT_BLUE,
) -> XChartDocument:
    try:
        # type check that diagram_name is ChartTemplateBase | str
        Info.is_type_enum_multi(
            alt_type="str", enum_type=ChartTemplateBase,
            enum_val=diagram_name, arg_name="diagram_name"
        )
        chart_name = Chart2._CHART_NAME + str(int(random() * 10_000))
        cls.add_table_chart(
            sheet=sheet,
            chart_name=chart_name,
            cells_range=cells_range,
            cell_name=cell_name,
            width=width,
            height=height,
        )
        chart_doc = cls.get_chart_doc(sheet, chart_name)

        # assign chart template to the chart's diagram
        diagram = chart_doc.getFirstDiagram()
        ct_template = cls.set_template(
            chart_doc=chart_doc, diagram=diagram, diagram_name=diagram_name
        )

        has_cats = cls.has_categories(diagram_name)

        dp = chart_doc.getDataProvider()

        ps = Props.make_props(
            CellRangeRepresentation=Calc.get_range_str(cells_range, sheet),
            DataRowSource=ChartDataRowSource.COLUMNS,
            FirstCellAsLabel=True,
            HasCategories=has_cats,
        )
        ds = dp.createDataSource(ps)

        # add data source to chart template
        args = Props.make_props(HasCategories=has_cats)
        ct_template.changeDiagramData(diagram, ds, args)

        # apply style settings to chart doc
        # background and wall colors
        cls.set_background_colors(chart_doc, color_bg, color_wall)

        if has_cats:
            cls.set_data_point_labels(chart_doc, DataPointLabelTypeKind.NUMBER)

        return chart_doc
    except ChartError:
        raise
    except Exception as e:
        raise ChartError("Error inserting chart") from e

insert_chart() creates a new chart document by calling add_table_chart() and get_chart_doc(), and then proceeds to link the chart template, diagram, and data source.

Get the Diagram

The chart diagram is the easiest to obtain, since it’s directly accessible via the XChartDocument reference:

# part of Chart2.insert_chart()...
diagram = chart_doc.getFirstDiagram() # XDiagram

Creating a Template

Creating a chart template is a few more steps. requiring the creation of a XChartTypeManager interface inside Chart2.set_template():

# in Chart2 class
@staticmethod
def set_template(
    chart_doc: XChartDocument, diagram: XDiagram, diagram_name: ChartTemplateBase | str
) -> XChartTypeTemplate:

    # ensure diagram_name is ChartTemplateBase | str
    Info.is_type_enum_multi(
        alt_type="str", enum_type=ChartTemplateBase, enum_val=diagram_name, arg_name="diagram_name"
    )

    try:
        ct_man = chart_doc.getChartTypeManager()
        msf = Lo.qi(XMultiServiceFactory, ct_man, True)
        template_nm = f"com.sun.star.chart2.template.{diagram_name}"
        ct_template = Lo.qi(XChartTypeTemplate, msf.createInstance(template_nm))
        if ct_template is None:
            Lo.print(
                f'Could not create chart template "{diagram_name}"; using a column chart instead'
            )
            ct_template = Lo.qi(
                XChartTypeTemplate, msf.createInstance("com.sun.star.chart2.template.Column"), True
            )

        ct_template.changeDiagram(diagram)
        return ct_template
    except Exception as e:
        raise ChartError("Error setting chart template") from e

The diagram_name value is one of the template names shown in Table 8 (i.e. Column). The string com.sun.star.chart2.template. is added to the front to create a fully qualified service name, which is then instantiated. If the instance creation fails, then the function falls back to creating an instance of the Column template. set_template() ends by calling XChartTypeTemplate.changeDiagram() which links the template to the chart’s diagram.

Get the Data Source

Back in Chart2.insert_chart(), the right-most branch of Fig. 234 involves the creation of an XDataProvider instance:

# part of Chart2.insert_chart()...
dp = chart_doc.getDataProvider() # XDataProvider

This data provider converts the chart’s data range into an XDataSource:

# part of Chart2.insert_chart()...
has_cats = cls.has_categories(diagram_name)

ps = Props.make_props(
    CellRangeRepresentation=Calc.get_range_str(cells_range, sheet),
    DataRowSource=ChartDataRowSource.COLUMNS,
    FirstCellAsLabel=True,
    HasCategories=has_cats,
)
ds = dp.createDataSource(ps) # XDataSource

The properties passed to XDataProvider.createDataSource() specify more details about the format of the data in Fig. 231 - the data for each graph is organized into columns with the first cell being the label for the graph. The HasCategories property is set to true when the first column of the data is to be used as x-axis categories.

These properties passed to createDataSource() are described in the documentation for the TabularDataProviderArguments service.

The has_cats boolean is set by examining the diagram name: if it’s an XY scatter chart or bubble chart then the first column of data will not be used as x-axis categories, so the boolean is set to False:

# in Chart2 class
@staticmethod
def has_categories(diagram_name: ChartTemplateBase | str) -> bool:
    # Ensure diagram_name ChartTemplateBase | str
    Info.is_type_enum_multi(
        alt_type="str", enum_type=ChartTemplateBase, enum_val=diagram_name, arg_name="diagram_name"
    )

    dn = str(diagram_name).lower()
    non_cats = ("scatter", "bubble")
    for non_cat in non_cats:
        if non_cat in dn:
            return False
    return True

Linking the template, diagram, and data source

Now the data source can populate the diagram using the specified chart template format:

# part of Chart2.insert_chart()...
# add data source to chart template
args = Props.make_props(HasCategories=has_cats)
ct_template.changeDiagramData(diagram, ds, args)

At this point the chart will be drawn in the Calc application window, and Chart2.insert_chart() could return. Instead my code modifies the appearance of the chart in two ways:

# part of Chart2.insert_chart()...
# apply style settings to chart doc
# background and wall colors
cls.set_background_colors(chart_doc, color_bg, color_wall)

if has_cats:  # charts using x-axis categories
    cls.set_data_point_labels(chart_doc, DataPointLabelTypeKind.NUMBER)

Chart2.set_background_colors() changes the background and wall colors of the chart (see Fig. 232). Chart2.set_data_point_labels() switches on the displaying of the y-axis data points as numbers which appear just above the top of each column in a column chart. The next section will describe how these methods work.

The call to Chart2.print_chart_types() at the end of Chart2.insert_chart() could be commented out since it’s a diagnostic check. It prints the names of the chart types used by the template.

28.3 Accessing and Modifying Chart Elements

Almost every aspect of a chart can be adjusted, including such things as its color scheme, the fonts, the scaling of the axes, the positioning of the legend, axis labels, and titles. It’s also possible to augment charts with regression line details, error bars, and additional graphs.

These elements are located in a number of different places in the hierarchy of services accessible through the ChartDocument service. A simplified version of this hierarchy is shown in Fig. 235.

The Hierarchy of Services Below ChartDocument

Fig. 235 :The Hierarchy of Services Below ChartDocument.

There is more information about the Diagram, CoordinateSystem, ChartType, and DataSeries services as this section progresses, but Fig. 235 indicates that Diagram manages the legend, floor and chart wall, CoordinateSystem is in charge of the axes, and the data points are manipulated via DataSeries.

The 1 and * in Fig. 235 indicate that a diagram may utilize multiple coordinate systems, that a single coordinate system may display multiple chart types, and a single chart type can employ many data series. Fortunately, this generality isn’t often needed for the charts created by Chart2.insert_chart(). In particular, the chart diagram only uses a single coordinate system and a single chart type (most of the time).

28.3.1 Accessing the Diagram

A chart’s Diagram service is easily reached by calling ChartDocument.getFirstDiagram(), which returns a reference to the diagram’s XDiagram interface:

XDiagram contains several useful methods (i.e. getLegend(), getWall(), getFloor()), and its services hold many properties (i.e. StartingAngle used in pie charts and RotationVertical for 3D charts). This is summarized by Fig. 236.

The Diagram Service.

Fig. 236 :The Diagram Service.

Chart2.set_background_colors() changes the background and wall colors of the chart through the ChartDocument and Diagram services:

# in Chart2 class
@staticmethod
def set_background_colors(
    chart_doc: XChartDocument, bg_color: mColor.Color, wall_color: mColor.Color
) -> None:
    try:
        if int(bg_color) > 0:
            bg_ps = chart_doc.getPageBackground()
            # Props.show_props("Background", bg_ps)
            Props.set(
                bg_ps, FillBackground=True, FillStyle=FillStyle.SOLID, FillColor=int(bg_color)
            )

        if int(wall_color) > 0:
            diagram = chart_doc.getFirstDiagram()
            wall_ps = diagram.getWall()
            # Props.show_props("Wall", wall_ps)
            Props.set(
                wall_ps, FillBackground=True, FillStyle=FillStyle.SOLID, FillColor=int(wall_color)
            )
    except Exception as e:
        raise ChartError("Error setting background colors") from e

The chart background is manipulated with a property set accessible through XChartDocument.getPageBackground(), while the wall is reached with XDiagram.getWall().

The documentation for the getPageBackground() and getWall() methods doesn’t list the contents of their property sets, so the easiest way of finding out what’s available is by calling Props.show_props(). Two show_props() calls are commented out in the code above.

Most chart services inherit a mix of four property classes:

Since getWall() and getPageBackground() both deal with areas in the chart, their properties come from the FillProperties class.

28.3.2 Accessing the Coordinate System

Fig. 236 shows that the diagram’s coordinate systems are reached through XCoordinateSystemContainer.getCoordinateSystems(). Chart2.get_coord_system() assumes that the programmer only wants the first coordinate system:

# in Chart2 class
@staticmethod
def get_coord_system(chart_doc: XChartDocument) -> XCoordinateSystem:
    try:
        diagram = chart_doc.getFirstDiagram()
        coord_sys_con = Lo.qi(XCoordinateSystemContainer, diagram, True)
        coord_sys = coord_sys_con.getCoordinateSystems()
        if coord_sys:
            if len(coord_sys) > 1:
                Lo.print(f"No. of coord systems: {len(coord_sys)}; using first.")
        return coord_sys[0]  # will raise error if coord_sys is empyt or none
    except Exception as e:
        raise ChartError("Error unable to get coord_system") from e

The CoordinateSystem service is employed to access the chart’s axes and its chart type (or types), as in Fig. 237.

The CoordinateSystem Service

Fig. 237 :The CoordinateSystem Service.

The Axis service is described when we look at methods for adjusting axis properties.

28.3.3 Accessing the Chart Type

Fig. 237 shows that the chart types in a coordinate system are reached through XChartTypeContainer.getChartTypes(). Chart2.get_chart_type() assumes the programmer only wants the first chart type in the array:

# in Chart2 class
@classmethod
def get_chart_type(cls, chart_doc: XChartDocument) -> XChartType:
    try:
        chart_types = cls.get_chart_types(chart_doc)
        return chart_types[0]
    except ChartError:
        raise
    except Exception as e:
        raise ChartError("Error getting chart type") from e

@classmethod
def get_chart_types(cls, chart_doc: XChartDocument) -> Tuple[XChartType, ...]:
    try:
        coord_sys = cls.get_coord_system(chart_doc)
        ct_con = Lo.qi(XChartTypeContainer, coord_sys, True)
        result = ct_con.getChartTypes()
        if result is None:
            raise UnKnownError("None Value: getChartTypes() returned a value of None")
        return result
    except ChartError:
        raise
    except Exception as e:
        raise ChartError("Error getting chart types") from e

Fig. 238 shows the main components of the ChartType service.

The ChartType Service

Fig. 238 :The ChartType Service.

Somewhat surprisingly, the ChartType service isn’t the home for chart type related properties; instead XChartType contains methods for examining chart type “roles”, which is described later. One useful features of XChartType is getChartType() which returns the type as a string.

The CandleStickChartType service inherits ChartType, and contains properties related to stock charts.

28.3.4 Accessing the Data Series

Fig. 238 shows that the data series for a chart type is accessed via XDataSeriesContainer.getDataSeries(). This is implemented by Chart2.get_data_series():

# in Chart2 class
@classmethod
def get_data_series(
    cls, chart_doc: XChartDocument, chart_type: ChartTypeNameBase | str = ""
) -> Tuple[XDataSeries, ...]:
    try:
        if chart_type:
            xchart_type = cls.find_chart_type(chart_doc, chart_type)
        else:
            xchart_type = cls.get_chart_type(chart_doc)
        ds_con = Lo.qi(XDataSeriesContainer, xchart_type, True)
        return ds_con.getDataSeries()
    except Exception as e:
        raise ChartError("Error getting chart data series") from e

The DataSeries service is one of the more complex parts of the Chart2 module because of its support for several important interfaces. They will not all be explained just yet; Fig. 239 focuses on the XDataSeries interface.

The DataSeries Service and XDataSeries Interface

Fig. 239 :The DataSeries Service and XDataSeries Interface.

A DataSeries represents a series of data points in the chart. Changes to the look of these data points (i.e. adding numbers next to the points, or changing their shape and color) can be done in two ways. A data series as a whole maintains a set of properties, most of which are inherited from the DataPointProperties class. Typical DataPointProperties values are Color, Shape, LineWidth.

It’s also possible to adjust point properties on an individual basis by accessing a particular data point by calling XDataSeries.getDataPointByIndex(). As the method name suggests, this requires an index value for the point, which can be a little tricky to determine.

Now we can explain the second of the two chart changing methods called at the end of Chart2.insert_chart(): Chart2.set_data_point_labels(), which switches on the displaying of the y-axis data points as numbers. The call is:

# part of Chart2.insert_chart()...
cls.set_data_point_labels(chart_doc, DataPointLabelTypeKind.NUMBER)

Chart2.set_data_point_labels() uses Chart2.get_data_series() described above, which returns an array of all the data series used in the chart. set_data_point_labels() iterates through the array and manipulates the Label property for each series. In other words, it modifies each data series

#
@classmethod
def set_data_point_labels(
    cls, chart_doc: XChartDocument, label_type: DataPointLabelTypeKind
) -> None:
    try:
        data_series_arr = cls.get_data_series(chart_doc=chart_doc)
        for data_series in data_series_arr:
            dp_label = cast(DataPointLabel, Props.get_property(data_series, "Label"))
            dp_label.ShowNumber = False
            dp_label.ShowCategoryName = False
            dp_label.ShowLegendSymbol = False
            if label_type == DataPointLabelTypeKind.NUMBER:
                dp_label.ShowNumber = True
            elif label_type == DataPointLabelTypeKind.PERCENT:
                dp_label.ShowNumber = True
                dp_label.ShowNumberInPercent = True
            elif label_type == DataPointLabelTypeKind.CATEGORY:
                dp_label.ShowCategoryName = True
            elif label_type == DataPointLabelTypeKind.SYMBOL:
                dp_label.ShowLegendSymbol = True
            elif label_type == DataPointLabelTypeKind.NONE:
                pass
            else:
                raise UnKnownError("label_type is of unknow type")

            Props.set_property(data_series, "Label", dp_label)
    except ChartError:
        raise
    except Exception as e:
        raise ChartError("Error setting data point labels") from e

The Label DataSeries property is inherited from DataPointProperties. Label is of type DataPointLabel which maintains four ‘show’ boolean values for displaying the number and other kinds of information next to the data point. Depending on the label_type value passed to Chart2.set_data_point_labels(), one or more of these boolean values are set and the Label property updated.